diff --git a/CTFd/admin/pages.py b/CTFd/admin/pages.py index a228002d..8fbe2a7d 100644 --- a/CTFd/admin/pages.py +++ b/CTFd/admin/pages.py @@ -27,7 +27,7 @@ def pages_preview(): data = request.form.to_dict() schema = PageSchema() page = schema.load(data) - return render_template("page.html", content=build_html(page.data["content"])) + return render_template("page.html", content=build_html(page.data.content)) @admin.route("/admin/pages/") diff --git a/CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue b/CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue new file mode 100644 index 00000000..aac46cd3 --- /dev/null +++ b/CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue @@ -0,0 +1,332 @@ + + + diff --git a/CTFd/themes/admin/assets/js/pages/editor.js b/CTFd/themes/admin/assets/js/pages/editor.js index 1f4fa71c..1f4d39bf 100644 --- a/CTFd/themes/admin/assets/js/pages/editor.js +++ b/CTFd/themes/admin/assets/js/pages/editor.js @@ -1,155 +1,11 @@ import "./main"; +import { showMediaLibrary } from "../styles"; import "core/utils"; import $ from "jquery"; import CTFd from "core/CTFd"; -import { default as helpers } from "core/helpers"; import CodeMirror from "codemirror"; import "codemirror/mode/htmlmixed/htmlmixed.js"; -import { ezQuery, ezToast } from "core/ezq"; - -function get_filetype_icon_class(filename) { - var mapping = { - // Image Files - png: "fa-file-image", - jpg: "fa-file-image", - jpeg: "fa-file-image", - gif: "fa-file-image", - bmp: "fa-file-image", - svg: "fa-file-image", - - // Text Files - txt: "fa-file-alt", - - // Video Files - mov: "fa-file-video", - mp4: "fa-file-video", - wmv: "fa-file-video", - flv: "fa-file-video", - mkv: "fa-file-video", - avi: "fa-file-video", - - // PDF Files - pdf: "fa-file-pdf", - - // Audio Files - mp3: "fa-file-sound", - wav: "fa-file-sound", - aac: "fa-file-sound", - - // Archive Files - zip: "fa-file-archive", - gz: "fa-file-archive", - tar: "fa-file-archive", - "7z": "fa-file-archive", - rar: "fa-file-archive", - - // Code Files - py: "fa-file-code", - c: "fa-file-code", - cpp: "fa-file-code", - html: "fa-file-code", - js: "fa-file-code", - rb: "fa-file-code", - go: "fa-file-code" - }; - - var ext = filename.split(".").pop(); - return mapping[ext]; -} - -function get_page_files() { - return CTFd.fetch("/api/v1/files?type=page", { - credentials: "same-origin" - }).then(function(response) { - return response.json(); - }); -} - -function show_files(data) { - var list = $("#media-library-list"); - list.empty(); - - for (var i = 0; i < data.length; i++) { - var f = data[i]; - var fname = f.location.split("/").pop(); - var ext = get_filetype_icon_class(f.location); - - var wrapper = $("
").attr("class", "media-item-wrapper"); - - var link = $(""); - link.attr("href", "##"); - - if (ext === undefined) { - link.append( - ' '.format(ext) - ); - } else { - link.append(' '.format(ext)); - } - - link.append( - $("") - .attr("class", "media-item-title") - .text(fname) - ); - - link.click(function(_e) { - var media_div = $(this).parent(); - var icon = $(this).find("i")[0]; - var f_loc = media_div.attr("data-location"); - var fname = media_div.attr("data-filename"); - var f_id = media_div.attr("data-id"); - $("#media-delete").attr("data-id", f_id); - $("#media-link").val(f_loc); - $("#media-filename").html( - $("") - .attr("href", f_loc) - .attr("target", "_blank") - .text(fname) - ); - - $("#media-icon").empty(); - if ($(icon).hasClass("fa-file-image")) { - $("#media-icon").append( - $("") - .attr("src", f_loc) - .css({ - "max-width": "100%", - "max-height": "100%", - "object-fit": "contain" - }) - ); - } else { - // icon is empty so we need to pull outerHTML - var copy_icon = $(icon).clone(); - $(copy_icon).addClass("fa-4x"); - $("#media-icon").append(copy_icon); - } - $("#media-item").show(); - }); - wrapper.append(link); - wrapper.attr("data-location", CTFd.config.urlRoot + "/files/" + f.location); - wrapper.attr("data-id", f.id); - wrapper.attr("data-filename", fname); - list.append(wrapper); - } -} - -function refresh_files(cb) { - get_page_files().then(function(response) { - var data = response.data; - show_files(data); - if (cb) { - cb(); - } - }); -} - -function insert_at_cursor(editor, text) { - var doc = editor.getDoc(); - var cursor = doc.getCursor(); - doc.replaceRange(text, cursor); -} +import { ezToast } from "core/ezq"; function submit_form() { // Save the CodeMirror data to the Textarea @@ -196,12 +52,6 @@ function preview_page() { $("#page-edit").submit(); } -function upload_media() { - helpers.files.upload($("#media-library-upload"), {}, function(_data) { - refresh_files(); - }); -} - $(() => { window.editor = CodeMirror.fromTextArea( document.getElementById("admin-pages-editor"), @@ -213,55 +63,8 @@ $(() => { } ); - $("#media-insert").click(function(_e) { - var tag = ""; - try { - tag = $("#media-icon") - .children()[0] - .nodeName.toLowerCase(); - } catch (err) { - tag = ""; - } - var link = $("#media-link").val(); - var fname = $("#media-filename").text(); - var entry = null; - if (tag === "img") { - entry = "![{0}]({1})".format(fname, link); - } else { - entry = "[{0}]({1})".format(fname, link); - } - insert_at_cursor(window.editor, entry); - }); - - $("#media-download").click(function(_e) { - var link = $("#media-link").val(); - window.open(link, "_blank"); - }); - - $("#media-delete").click(function(_e) { - var file_id = $(this).attr("data-id"); - ezQuery({ - title: "Delete File?", - body: "Are you sure you want to delete this file?", - success: function() { - CTFd.fetch("/api/v1/files/" + file_id, { - method: "DELETE", - credentials: "same-origin", - headers: { - Accept: "application/json", - "Content-Type": "application/json" - } - }).then(function(response) { - if (response.status === 200) { - response.json().then(function(object) { - if (object.success) { - refresh_files(); - } - }); - } - }); - } - }); + $("#media-button").click(function(_e) { + showMediaLibrary(window.editor); }); $("#save-page").click(function(e) { @@ -269,17 +72,6 @@ $(() => { submit_form(); }); - $("#media-button").click(function() { - $("#media-library-list").empty(); - refresh_files(function() { - $("#media-modal").modal(); - }); - }); - - $(".media-upload-button").click(function() { - upload_media(); - }); - $(".preview-page").click(function() { preview_page(); }); diff --git a/CTFd/themes/admin/assets/js/styles.js b/CTFd/themes/admin/assets/js/styles.js index 40b50ea7..d8bf4753 100644 --- a/CTFd/themes/admin/assets/js/styles.js +++ b/CTFd/themes/admin/assets/js/styles.js @@ -2,6 +2,33 @@ import "bootstrap/dist/js/bootstrap.bundle"; import { makeSortableTables } from "core/utils"; import $ from "jquery"; import EasyMDE from "easymde"; +import Vue from "vue/dist/vue.esm.browser"; +import MediaLibrary from "./components/files/MediaLibrary.vue"; + +export function showMediaLibrary(editor) { + const mediaModal = Vue.extend(MediaLibrary); + + // Create an empty div and append it to our
+ let vueContainer = document.createElement("div"); + document.querySelector("main").appendChild(vueContainer); + + // Create MediaLibrary component and pass it our editor + let m = new mediaModal({ + propsData: { + editor: editor + } + // Mount to the empty div + }).$mount(vueContainer); + + // Destroy the Vue instance and the media modal when closed + $("#media-modal").on("hidden.bs.modal", function(_e) { + m.$destroy(); + $("#media-modal").remove(); + }); + + // Pop the Component modal + $("#media-modal").modal(); +} export function bindMarkdownEditors() { $("textarea.markdown").each(function(_i, e) { @@ -19,6 +46,14 @@ export function bindMarkdownEditors() { "|", "link", "image", + { + name: "media", + action: editor => { + showMediaLibrary(editor); + }, + className: "fas fa-file-upload", + title: "Media Library" + }, "|", "preview", "guide" diff --git a/CTFd/themes/admin/static/js/core.dev.js b/CTFd/themes/admin/static/js/core.dev.js index 2572a3ff..3a0dd9f4 100644 --- a/CTFd/themes/admin/static/js/core.dev.js +++ b/CTFd/themes/admin/static/js/core.dev.js @@ -1,5 +1,41 @@ (window["webpackJsonp"] = window["webpackJsonp"] || []).push([["default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"],{ +/***/ "./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue": +/*!***********************************************************************!*\ + !*** ./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue ***! + \***********************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _MediaLibrary_vue_vue_type_template_id_50f8d42a___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./MediaLibrary.vue?vue&type=template&id=50f8d42a& */ \"./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=template&id=50f8d42a&\");\n/* harmony import */ var _MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./MediaLibrary.vue?vue&type=script&lang=js& */ \"./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=script&lang=js&\");\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__) if(__WEBPACK_IMPORT_KEY__ !== 'default') (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _MediaLibrary_vue_vue_type_template_id_50f8d42a___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _MediaLibrary_vue_vue_type_template_id_50f8d42a___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?"); + +/***/ }), + +/***/ "./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=script&lang=js&": +/*!************************************************************************************************!*\ + !*** ./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=script&lang=js& ***! + \************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../../../node_modules/babel-loader/lib??ref--0!../../../../../../../node_modules/vue-loader/lib??vue-loader-options!./MediaLibrary.vue?vue&type=script&lang=js& */ \"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=script&lang=js&\");\n/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== 'default') (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0___default.a); \n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?"); + +/***/ }), + +/***/ "./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=template&id=50f8d42a&": +/*!******************************************************************************************************!*\ + !*** ./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=template&id=50f8d42a& ***! + \******************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_template_id_50f8d42a___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../../../../node_modules/vue-loader/lib??vue-loader-options!./MediaLibrary.vue?vue&type=template&id=50f8d42a& */ \"./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=template&id=50f8d42a&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_template_id_50f8d42a___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_MediaLibrary_vue_vue_type_template_id_50f8d42a___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?"); + +/***/ }), + /***/ "./CTFd/themes/admin/assets/js/pages/main.js": /*!***************************************************!*\ !*** ./CTFd/themes/admin/assets/js/pages/main.js ***! @@ -20,7 +56,7 @@ eval("\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd * /***/ (function(module, exports, __webpack_require__) { ; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.bindMarkdownEditors = bindMarkdownEditors;\nexports.default = void 0;\n\n__webpack_require__(/*! bootstrap/dist/js/bootstrap.bundle */ \"./node_modules/bootstrap/dist/js/bootstrap.bundle.js\");\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _easymde = _interopRequireDefault(__webpack_require__(/*! easymde */ \"./node_modules/easymde/src/js/easymde.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction bindMarkdownEditors() {\n (0, _jquery.default)(\"textarea.markdown\").each(function (_i, e) {\n if (e.hasOwnProperty(\"mde\") === false) {\n var mde = new _easymde.default({\n autoDownloadFontAwesome: false,\n toolbar: [\"bold\", \"italic\", \"heading\", \"|\", \"quote\", \"unordered-list\", \"ordered-list\", \"|\", \"link\", \"image\", \"|\", \"preview\", \"guide\"],\n element: this,\n initialValue: (0, _jquery.default)(this).val(),\n forceSync: true,\n minHeight: \"200px\"\n });\n this.mde = mde;\n this.codemirror = mde.codemirror;\n (0, _jquery.default)(this).on(\"change keyup paste\", function () {\n mde.codemirror.getDoc().setValue((0, _jquery.default)(this).val());\n mde.codemirror.refresh();\n });\n }\n });\n}\n\nvar _default = function _default() {\n // TODO: This is kind of a hack to mimic a React-like state construct.\n // It should be removed once we have a real front-end framework in place.\n (0, _jquery.default)(\":input\").each(function () {\n (0, _jquery.default)(this).data(\"initial\", (0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(function () {\n (0, _jquery.default)(\"tr[data-href], td[data-href]\").click(function () {\n var sel = getSelection().toString();\n\n if (!sel) {\n var href = (0, _jquery.default)(this).attr(\"data-href\");\n\n if (href) {\n window.location = href;\n }\n }\n\n return false;\n });\n (0, _jquery.default)(\"[data-checkbox]\").click(function (e) {\n if ((0, _jquery.default)(e.target).is(\"input[type=checkbox]\")) {\n e.stopImmediatePropagation();\n return;\n }\n\n var checkbox = (0, _jquery.default)(this).find(\"input[type=checkbox]\"); // Doing it this way with an event allows data-checkbox-all to work\n\n checkbox.click();\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"[data-checkbox-all]\").on(\"click change\", function (e) {\n var checked = (0, _jquery.default)(this).prop(\"checked\");\n var idx = (0, _jquery.default)(this).index() + 1;\n (0, _jquery.default)(this).closest(\"table\").find(\"tr td:nth-child(\".concat(idx, \") input[type=checkbox]\")).prop(\"checked\", checked);\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"tr[data-href] a, tr[data-href] button\").click(function (e) {\n // TODO: This is a hack to allow modal close buttons to work\n if (!(0, _jquery.default)(this).attr(\"data-dismiss\")) {\n e.stopPropagation();\n }\n });\n (0, _jquery.default)(\".page-select\").change(function () {\n var url = new URL(window.location);\n url.searchParams.set(\"page\", this.value);\n window.location.href = url.toString();\n });\n (0, _jquery.default)('a[data-toggle=\"tab\"]').on(\"shown.bs.tab\", function (e) {\n sessionStorage.setItem(\"activeTab\", (0, _jquery.default)(e.target).attr(\"href\"));\n });\n var activeTab = sessionStorage.getItem(\"activeTab\");\n\n if (activeTab) {\n var target = (0, _jquery.default)(\".nav-tabs a[href=\\\"\".concat(activeTab, \"\\\"], .nav-pills a[href=\\\"\").concat(activeTab, \"\\\"]\"));\n\n if (target.length) {\n target.tab(\"show\");\n } else {\n sessionStorage.removeItem(\"activeTab\");\n }\n }\n\n bindMarkdownEditors();\n (0, _utils.makeSortableTables)();\n (0, _jquery.default)('[data-toggle=\"tooltip\"]').tooltip();\n });\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/styles.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.showMediaLibrary = showMediaLibrary;\nexports.bindMarkdownEditors = bindMarkdownEditors;\nexports.default = void 0;\n\n__webpack_require__(/*! bootstrap/dist/js/bootstrap.bundle */ \"./node_modules/bootstrap/dist/js/bootstrap.bundle.js\");\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _easymde = _interopRequireDefault(__webpack_require__(/*! easymde */ \"./node_modules/easymde/src/js/easymde.js\"));\n\nvar _vueEsm = _interopRequireDefault(__webpack_require__(/*! vue/dist/vue.esm.browser */ \"./node_modules/vue/dist/vue.esm.browser.js\"));\n\nvar _MediaLibrary = _interopRequireDefault(__webpack_require__(/*! ./components/files/MediaLibrary.vue */ \"./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction showMediaLibrary(editor) {\n var mediaModal = _vueEsm.default.extend(_MediaLibrary.default); // Create an empty div and append it to our
\n\n\n var vueContainer = document.createElement(\"div\");\n document.querySelector(\"main\").appendChild(vueContainer); // Create MediaLibrary component and pass it our editor\n\n var m = new mediaModal({\n propsData: {\n editor: editor // Mount to the empty div\n\n }\n }).$mount(vueContainer); // Destroy the Vue instance and the media modal when closed\n\n (0, _jquery.default)(\"#media-modal\").on(\"hidden.bs.modal\", function (_e) {\n m.$destroy();\n (0, _jquery.default)(\"#media-modal\").remove();\n }); // Pop the Component modal\n\n (0, _jquery.default)(\"#media-modal\").modal();\n}\n\nfunction bindMarkdownEditors() {\n (0, _jquery.default)(\"textarea.markdown\").each(function (_i, e) {\n if (e.hasOwnProperty(\"mde\") === false) {\n var mde = new _easymde.default({\n autoDownloadFontAwesome: false,\n toolbar: [\"bold\", \"italic\", \"heading\", \"|\", \"quote\", \"unordered-list\", \"ordered-list\", \"|\", \"link\", \"image\", {\n name: \"media\",\n action: function action(editor) {\n showMediaLibrary(editor);\n },\n className: \"fas fa-file-upload\",\n title: \"Media Library\"\n }, \"|\", \"preview\", \"guide\"],\n element: this,\n initialValue: (0, _jquery.default)(this).val(),\n forceSync: true,\n minHeight: \"200px\"\n });\n this.mde = mde;\n this.codemirror = mde.codemirror;\n (0, _jquery.default)(this).on(\"change keyup paste\", function () {\n mde.codemirror.getDoc().setValue((0, _jquery.default)(this).val());\n mde.codemirror.refresh();\n });\n }\n });\n}\n\nvar _default = function _default() {\n // TODO: This is kind of a hack to mimic a React-like state construct.\n // It should be removed once we have a real front-end framework in place.\n (0, _jquery.default)(\":input\").each(function () {\n (0, _jquery.default)(this).data(\"initial\", (0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(function () {\n (0, _jquery.default)(\"tr[data-href], td[data-href]\").click(function () {\n var sel = getSelection().toString();\n\n if (!sel) {\n var href = (0, _jquery.default)(this).attr(\"data-href\");\n\n if (href) {\n window.location = href;\n }\n }\n\n return false;\n });\n (0, _jquery.default)(\"[data-checkbox]\").click(function (e) {\n if ((0, _jquery.default)(e.target).is(\"input[type=checkbox]\")) {\n e.stopImmediatePropagation();\n return;\n }\n\n var checkbox = (0, _jquery.default)(this).find(\"input[type=checkbox]\"); // Doing it this way with an event allows data-checkbox-all to work\n\n checkbox.click();\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"[data-checkbox-all]\").on(\"click change\", function (e) {\n var checked = (0, _jquery.default)(this).prop(\"checked\");\n var idx = (0, _jquery.default)(this).index() + 1;\n (0, _jquery.default)(this).closest(\"table\").find(\"tr td:nth-child(\".concat(idx, \") input[type=checkbox]\")).prop(\"checked\", checked);\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"tr[data-href] a, tr[data-href] button\").click(function (e) {\n // TODO: This is a hack to allow modal close buttons to work\n if (!(0, _jquery.default)(this).attr(\"data-dismiss\")) {\n e.stopPropagation();\n }\n });\n (0, _jquery.default)(\".page-select\").change(function () {\n var url = new URL(window.location);\n url.searchParams.set(\"page\", this.value);\n window.location.href = url.toString();\n });\n (0, _jquery.default)('a[data-toggle=\"tab\"]').on(\"shown.bs.tab\", function (e) {\n sessionStorage.setItem(\"activeTab\", (0, _jquery.default)(e.target).attr(\"href\"));\n });\n var activeTab = sessionStorage.getItem(\"activeTab\");\n\n if (activeTab) {\n var target = (0, _jquery.default)(\".nav-tabs a[href=\\\"\".concat(activeTab, \"\\\"], .nav-pills a[href=\\\"\").concat(activeTab, \"\\\"]\"));\n\n if (target.length) {\n target.tab(\"show\");\n } else {\n sessionStorage.removeItem(\"activeTab\");\n }\n }\n\n bindMarkdownEditors();\n (0, _utils.makeSortableTables)();\n (0, _jquery.default)('[data-toggle=\"tooltip\"]').tooltip();\n });\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/styles.js?"); /***/ }), @@ -132,6 +168,18 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ }), +/***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=script&lang=js&": +/*!******************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=script&lang=js& ***! + \******************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +; +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _helpers = _interopRequireDefault(__webpack_require__(/*! core/helpers */ \"./CTFd/themes/core/assets/js/helpers.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nfunction get_page_files() {\n return _CTFd.default.fetch(\"/api/v1/files?type=page\", {\n credentials: \"same-origin\"\n }).then(function (response) {\n return response.json();\n });\n}\n\nvar _default = {\n props: {\n editor: Object\n },\n data: function data() {\n return {\n files: [],\n selectedFile: null\n };\n },\n methods: {\n getPageFiles: function getPageFiles() {\n var _this = this;\n\n get_page_files().then(function (response) {\n _this.files = response.data;\n return _this.files;\n });\n },\n uploadChosenFiles: function uploadChosenFiles() {\n var _this2 = this;\n\n // TODO: We should reduce the need to interact with the DOM directly.\n // This looks jank and we should be able to remove it.\n var form = document.querySelector(\"#media-library-upload\");\n\n _helpers.default.files.upload(form, {}, function (_data) {\n _this2.getPageFiles();\n });\n },\n selectFile: function selectFile(file) {\n this.selectedFile = file;\n return this.selectedFile;\n },\n buildSelectedFileUrl: function buildSelectedFileUrl() {\n return _CTFd.default.config.urlRoot + \"/files/\" + this.selectedFile.location;\n },\n deleteSelectedFile: function deleteSelectedFile() {\n var _this3 = this;\n\n var file_id = this.selectedFile.id;\n\n if (confirm(\"Are you sure you want to delete this file?\")) {\n _CTFd.default.fetch(\"/api/v1/files/\" + file_id, {\n method: \"DELETE\"\n }).then(function (response) {\n if (response.status === 200) {\n response.json().then(function (object) {\n if (object.success) {\n _this3.getPageFiles();\n\n _this3.selectedFile = null;\n }\n });\n }\n });\n }\n },\n insertSelectedFile: function insertSelectedFile() {\n var editor = this.$props.editor;\n\n if (editor.hasOwnProperty(\"codemirror\")) {\n editor = editor.codemirror;\n }\n\n var doc = editor.getDoc();\n var cursor = doc.getCursor();\n var url = this.buildSelectedFileUrl();\n var img = this.getIconClass(this.selectedFile.location) === \"far fa-file-image\";\n var filename = url.split(\"/\").pop();\n link = \"[{0}]({1})\".format(filename, url);\n\n if (img) {\n link = \"!\" + link;\n }\n\n doc.replaceRange(link, cursor);\n },\n downloadSelectedFile: function downloadSelectedFile() {\n var link = this.buildSelectedFileUrl();\n window.open(link, \"_blank\");\n },\n getIconClass: function getIconClass(filename) {\n var mapping = {\n // Image Files\n png: \"far fa-file-image\",\n jpg: \"far fa-file-image\",\n jpeg: \"far fa-file-image\",\n gif: \"far fa-file-image\",\n bmp: \"far fa-file-image\",\n svg: \"far fa-file-image\",\n // Text Files\n txt: \"far fa-file-alt\",\n // Video Files\n mov: \"far fa-file-video\",\n mp4: \"far fa-file-video\",\n wmv: \"far fa-file-video\",\n flv: \"far fa-file-video\",\n mkv: \"far fa-file-video\",\n avi: \"far fa-file-video\",\n // PDF Files\n pdf: \"far fa-file-pdf\",\n // Audio Files\n mp3: \"far fa-file-sound\",\n wav: \"far fa-file-sound\",\n aac: \"far fa-file-sound\",\n // Archive Files\n zip: \"far fa-file-archive\",\n gz: \"far fa-file-archive\",\n tar: \"far fa-file-archive\",\n \"7z\": \"far fa-file-archive\",\n rar: \"far fa-file-archive\",\n // Code Files\n py: \"far fa-file-code\",\n c: \"far fa-file-code\",\n cpp: \"far fa-file-code\",\n html: \"far fa-file-code\",\n js: \"far fa-file-code\",\n rb: \"far fa-file-code\",\n go: \"far fa-file-code\"\n };\n var ext = filename.split(\".\").pop();\n return mapping[ext] || \"far fa-file\";\n }\n },\n created: function created() {\n return this.getPageFiles();\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + /***/ "./node_modules/moment/locale sync recursive ^\\.\\/.*$": /*!**************************************************!*\ !*** ./node_modules/moment/locale sync ^\.\/.*$ ***! @@ -143,6 +191,18 @@ eval("var map = {\n\t\"./af\": \"./node_modules/moment/locale/af.js\",\n\t\"./af /***/ }), +/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=template&id=50f8d42a&": +/*!************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?vue&type=template&id=50f8d42a& ***! + \************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"modal fade\", attrs: { id: \"media-modal\", tabindex: \"-1\" } },\n [\n _c(\"div\", { staticClass: \"modal-dialog modal-lg\" }, [\n _c(\"div\", { staticClass: \"modal-content\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"modal-body\" }, [\n _c(\"div\", { staticClass: \"modal-header\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row mh-100\" }, [\n _c(\n \"div\",\n {\n staticClass: \"col-md-6\",\n attrs: { id: \"media-library-list\" }\n },\n _vm._l(_vm.files, function(file) {\n return _c(\n \"div\",\n { key: file.id, staticClass: \"media-item-wrapper\" },\n [\n _c(\n \"a\",\n {\n attrs: { href: \"javascript:void(0)\" },\n on: {\n click: function($event) {\n _vm.selectFile(file)\n return false\n }\n }\n },\n [\n _c(\"i\", {\n class: _vm.getIconClass(file.location),\n attrs: { \"aria-hidden\": \"true\" }\n }),\n _vm._v(\" \"),\n _c(\"small\", { staticClass: \"media-item-title\" }, [\n _vm._v(_vm._s(file.location.split(\"/\").pop()))\n ])\n ]\n )\n ]\n )\n }),\n 0\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n {\n staticClass: \"col-md-6\",\n attrs: { id: \"media-library-details\" }\n },\n [\n _c(\"h4\", { staticClass: \"text-center\" }, [\n _vm._v(\"Media Details\")\n ]),\n _vm._v(\" \"),\n _c(\"div\", { attrs: { id: \"media-item\" } }, [\n _c(\n \"div\",\n {\n staticClass: \"text-center\",\n attrs: { id: \"media-icon\" }\n },\n [\n this.selectedFile\n ? _c(\"div\", [\n _vm.getIconClass(\n this.selectedFile.location\n ) === \"far fa-file-image\"\n ? _c(\"div\", [\n _c(\"img\", {\n staticStyle: {\n \"max-width\": \"100%\",\n \"max-height\": \"100%\",\n \"object-fit\": \"contain\"\n },\n attrs: {\n src: _vm.buildSelectedFileUrl()\n }\n })\n ])\n : _c(\"div\", [\n _c(\"i\", {\n class:\n _vm.getIconClass(\n this.selectedFile.location\n ) + \" fa-4x\",\n attrs: { \"aria-hidden\": \"true\" }\n })\n ])\n ])\n : _vm._e()\n ]\n ),\n _vm._v(\" \"),\n _c(\"br\"),\n _vm._v(\" \"),\n this.selectedFile\n ? _c(\n \"div\",\n {\n staticClass: \"text-center\",\n attrs: { id: \"media-filename\" }\n },\n [\n _c(\n \"a\",\n {\n attrs: {\n href: _vm.buildSelectedFileUrl(),\n target: \"_blank\"\n }\n },\n [\n _vm._v(\n \"\\n \" +\n _vm._s(\n this.selectedFile.location\n .split(\"/\")\n .pop()\n ) +\n \"\\n \"\n )\n ]\n )\n ]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n this.selectedFile\n ? _c(\"div\", [\n _vm._v(\n \"\\n Link:\\n \"\n ),\n _c(\"input\", {\n staticClass: \"form-control\",\n attrs: {\n type: \"text\",\n id: \"media-link\",\n readonly: \"\"\n },\n domProps: {\n value: _vm.buildSelectedFileUrl()\n }\n })\n ])\n : _c(\"div\", [\n _vm._v(\n \"\\n Link:\\n \"\n ),\n _c(\"input\", {\n staticClass: \"form-control\",\n attrs: {\n type: \"text\",\n id: \"media-link\",\n readonly: \"\"\n }\n })\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group text-center\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-6\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-success w-100\",\n attrs: {\n id: \"media-insert\",\n \"data-toggle\": \"tooltip\",\n \"data-placement\": \"top\",\n title: \"Insert link into editor\"\n },\n on: { click: _vm.insertSelectedFile }\n },\n [\n _vm._v(\n \"\\n Insert\\n \"\n )\n ]\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-3\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-primary w-100\",\n attrs: {\n id: \"media-download\",\n \"data-toggle\": \"tooltip\",\n \"data-placement\": \"top\",\n title: \"Download file\"\n },\n on: { click: _vm.downloadSelectedFile }\n },\n [_c(\"i\", { staticClass: \"fas fa-download\" })]\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-3\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-danger w-100\",\n attrs: {\n id: \"media-delete\",\n \"data-toggle\": \"tooltip\",\n \"data-placement\": \"top\",\n title: \"Delete file\"\n },\n on: { click: _vm.deleteSelectedFile }\n },\n [_c(\"i\", { staticClass: \"far fa-trash-alt\" })]\n )\n ])\n ])\n ])\n ])\n ]\n )\n ])\n ])\n ]),\n _vm._v(\" \"),\n _vm._m(1)\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"modal-footer\" }, [\n _c(\"div\", { staticClass: \"float-right\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-primary media-upload-button\",\n attrs: { type: \"submit\" },\n on: { click: _vm.uploadChosenFiles }\n },\n [_vm._v(\"\\n Upload\\n \")]\n )\n ])\n ])\n ])\n ])\n ]\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-header\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"h3\", { staticClass: \"text-center\" }, [_vm._v(\"Media Library\")])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n staticClass: \"close\",\n attrs: {\n type: \"button\",\n \"data-dismiss\": \"modal\",\n \"aria-label\": \"Close\"\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"form\",\n { attrs: { id: \"media-library-upload\", enctype: \"multipart/form-data\" } },\n [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", { attrs: { for: \"media-files\" } }, [\n _vm._v(\"\\n Upload Files\\n \")\n ]),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"form-control-file\",\n attrs: {\n type: \"file\",\n name: \"file\",\n id: \"media-files\",\n multiple: \"\"\n }\n }),\n _vm._v(\" \"),\n _c(\"sub\", { staticClass: \"help-block\" }, [\n _vm._v(\n \"\\n Attach multiple files using Control+Click or Cmd+Click.\\n \"\n )\n ])\n ]),\n _vm._v(\" \"),\n _c(\"input\", { attrs: { type: \"hidden\", value: \"page\", name: \"type\" } })\n ]\n )\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + /***/ 0: /*!********************!*\ !*** fs (ignored) ***! diff --git a/CTFd/themes/admin/static/js/pages/challenge.min.js b/CTFd/themes/admin/static/js/pages/challenge.min.js index cdec9516..6634d660 100644 --- a/CTFd/themes/admin/static/js/pages/challenge.min.js +++ b/CTFd/themes/admin/static/js/pages/challenge.min.js @@ -1 +1 @@ -!function(d){function e(e){for(var t,o,n=e[0],a=e[1],s=e[2],i=0,l=[];i -- ");for(var a in o.append(n),t)t.hasOwnProperty(a)&&(n=(0,s.default)("".format(a,t[a].name)),o.append(n));(0,s.default)("#flag-edit-modal").modal()}),(0,s.default)("#flag-edit-modal form").submit(function(e){e.preventDefault();var t=(0,s.default)(this).serializeJSON(!0);t.challenge=window.CHALLENGE_ID,i.default.fetch("/api/v1/flags",{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(e){window.location.reload()})}),(0,s.default)("#flag-edit-modal").modal()},t.editFlagModal=function(e){e.preventDefault();var n=(0,s.default)(this).attr("flag-id"),a=(0,s.default)(this).parent().parent();s.default.get(i.default.config.urlRoot+"/api/v1/flags/"+n,function(e){var o=e.data;s.default.get(i.default.config.urlRoot+o.templates.update,function(e){(0,s.default)("#edit-flags form").empty(),(0,s.default)("#edit-flags form").off();var t=l.default.compile(e);(0,s.default)("#edit-flags form").append(t.render(o)),(0,s.default)("#edit-flags form").submit(function(e){e.preventDefault();var t=(0,s.default)("#edit-flags form").serializeJSON();i.default.fetch("/api/v1/flags/"+n,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(e){e.success&&((0,s.default)(a).find(".flag-content").text(e.data.content),(0,s.default)("#edit-flags").modal("toggle"))})}),(0,s.default)("#edit-flags").modal()})})},t.flagTypeSelect=function(e){e.preventDefault();var t=(0,s.default)(this).find("option:selected").text();s.default.get(i.default.config.urlRoot+"/api/v1/flags/types/"+t,function(e){var t=e.data;s.default.get(i.default.config.urlRoot+t.templates.create,function(e){var t=l.default.compile(e);(0,s.default)("#create-keys-entry-div").html(t.render()),(0,s.default)("#create-keys-button-div").show()})})};var s=a(o("./node_modules/jquery/dist/jquery.js")),i=a(o("./CTFd/themes/core/assets/js/CTFd.js")),l=a(o("./node_modules/nunjucks/browser/nunjucks.js")),n=o("./CTFd/themes/core/assets/js/ezq.js");function a(e){return e&&e.__esModule?e:{default:e}}},"./CTFd/themes/admin/assets/js/challenges/hints.js":function(e,t,o){Object.defineProperty(t,"__esModule",{value:!0}),t.showHintModal=function(e){e.preventDefault(),(0,a.default)("#hint-edit-modal form").find("input, textarea").val("").trigger("change"),(0,a.default)("#hint-edit-form textarea").each(function(e,t){t.hasOwnProperty("codemirror")&&t.codemirror.refresh()}),(0,a.default)("#hint-edit-modal").modal()},t.showEditHintModal=function(e){e.preventDefault();var t=(0,a.default)(this).attr("hint-id");s.default.fetch("/api/v1/hints/"+t+"?preview=true",{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&((0,a.default)("#hint-edit-form input[name=content],textarea[name=content]").val(e.data.content).trigger("change"),(0,a.default)("#hint-edit-modal").on("shown.bs.modal",function(){(0,a.default)("#hint-edit-form textarea").each(function(e,t){t.hasOwnProperty("codemirror")&&t.codemirror.refresh()})}).on("hide.bs.modal",function(){(0,a.default)("#hint-edit-form textarea").each(function(e,t){(0,a.default)(t).val("").trigger("change"),t.hasOwnProperty("codemirror")&&t.codemirror.refresh()})}),(0,a.default)("#hint-edit-form input[name=cost]").val(e.data.cost),(0,a.default)("#hint-edit-form input[name=id]").val(e.data.id),(0,a.default)("#hint-edit-modal").modal())})},t.deleteHint=function(e){e.preventDefault();var t=(0,a.default)(this).attr("hint-id"),o=(0,a.default)(this).parent().parent();(0,n.ezQuery)({title:"Delete Hint",body:"Are you sure you want to delete this hint?",success:function(){s.default.fetch("/api/v1/hints/"+t,{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&o.remove()})}})},t.editHint=function(e){e.preventDefault();var t=(0,a.default)(this).serializeJSON(!0);t.challenge=window.CHALLENGE_ID;var o="POST",n="/api/v1/hints";t.id&&(o="PATCH",n="/api/v1/hints/"+t.id);s.default.fetch(n,{method:o,credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(e){e.success&&window.location.reload()})};var a=i(o("./node_modules/jquery/dist/jquery.js")),s=i(o("./CTFd/themes/core/assets/js/CTFd.js")),n=o("./CTFd/themes/core/assets/js/ezq.js");function i(e){return e&&e.__esModule?e:{default:e}}},"./CTFd/themes/admin/assets/js/challenges/requirements.js":function(e,t,o){Object.defineProperty(t,"__esModule",{value:!0}),t.addRequirement=function(e){e.preventDefault();var t=(0,a.default)("#prerequisite-add-form").serializeJSON();if(!t.prerequisite)return;window.CHALLENGE_REQUIREMENTS.prerequisites.push(parseInt(t.prerequisite));var o={requirements:window.CHALLENGE_REQUIREMENTS};s.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(o)}).then(function(e){return e.json()}).then(function(e){e.success&&window.location.reload()})},t.deleteRequirement=function(e){var t=(0,a.default)(this).attr("challenge-id"),o=(0,a.default)(this).parent().parent();window.CHALLENGE_REQUIREMENTS.prerequisites.pop(t);var n={requirements:window.CHALLENGE_REQUIREMENTS};s.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(n)}).then(function(e){return e.json()}).then(function(e){e.success&&o.remove()})};var a=n(o("./node_modules/jquery/dist/jquery.js")),s=n(o("./CTFd/themes/core/assets/js/CTFd.js"));function n(e){return e&&e.__esModule?e:{default:e}}},"./CTFd/themes/admin/assets/js/challenges/tags.js":function(e,t,o){Object.defineProperty(t,"__esModule",{value:!0}),t.deleteTag=i,t.addTag=function(e){if(13!=e.keyCode)return;var t=(0,n.default)(this),o={value:t.val(),challenge:window.CHALLENGE_ID};a.default.api.post_tag_list({},o).then(function(e){if(e.success){var t=(0,n.default)("{0}×".format(e.data.value,e.data.id));(0,n.default)("#challenge-tags").append(t),t.click(i)}}),t.val("")};var n=s(o("./node_modules/jquery/dist/jquery.js")),a=s(o("./CTFd/themes/core/assets/js/CTFd.js"));function s(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=(0,n.default)(this),o=t.attr("tag-id");a.default.api.delete_tag({tagId:o}).then(function(e){e.success&&t.parent().remove()})}},"./CTFd/themes/admin/assets/js/pages/challenge.js":function(e,t,o){o("./CTFd/themes/admin/assets/js/pages/main.js");var n=o("./CTFd/themes/core/assets/js/utils.js"),l=f(o("./node_modules/jquery/dist/jquery.js"));o("./node_modules/bootstrap/js/dist/tab.js");var i=f(o("./CTFd/themes/core/assets/js/CTFd.js")),a=o("./CTFd/themes/core/assets/js/ezq.js"),d=f(o("./CTFd/themes/core/assets/js/helpers.js")),s=o("./CTFd/themes/admin/assets/js/challenges/files.js"),r=o("./CTFd/themes/admin/assets/js/challenges/tags.js"),c=o("./CTFd/themes/admin/assets/js/challenges/requirements.js"),u=o("./CTFd/themes/admin/assets/js/styles.js"),m=o("./CTFd/themes/admin/assets/js/challenges/hints.js"),p=o("./CTFd/themes/admin/assets/js/challenges/flags.js");function f(e){return e&&e.__esModule?e:{default:e}}function h(e){i.default.api.get_hint({hintId:e,preview:!0}).then(function(e){e.data.content&&function(e){(0,a.ezAlert)({title:"Hint",body:j.render(e.content),button:"Got it!"})}(e.data)})}var j=i.default.lib.markdown();function g(e,t){var o=e.data,n=(0,l.default)("#result-message"),a=(0,l.default)("#result-notification"),s=(0,l.default)("#submission-input");a.removeClass(),n.text(o.message),"authentication_required"!==o.status?("incorrect"===o.status?(a.addClass("alert alert-danger alert-dismissable text-center"),a.slideDown(),s.removeClass("correct"),s.addClass("wrong"),setTimeout(function(){s.removeClass("wrong")},3e3)):"correct"===o.status?(a.addClass("alert alert-success alert-dismissable text-center"),a.slideDown(),(0,l.default)(".challenge-solves").text(parseInt((0,l.default)(".challenge-solves").text().split(" ")[0])+1+" Solves"),s.val(""),s.removeClass("wrong"),s.addClass("correct")):"already_solved"===o.status?(a.addClass("alert alert-info alert-dismissable text-center"),a.slideDown(),s.addClass("correct")):"paused"===o.status?(a.addClass("alert alert-warning alert-dismissable text-center"),a.slideDown()):"ratelimited"===o.status&&(a.addClass("alert alert-warning alert-dismissable text-center"),a.slideDown(),s.addClass("too-fast"),setTimeout(function(){s.removeClass("too-fast")},3e3)),setTimeout(function(){(0,l.default)(".alert").slideUp(),(0,l.default)("#submit-key").removeClass("disabled-button"),(0,l.default)("#submit-key").prop("disabled",!1)},3e3),t&&t(o)):window.location=i.default.config.urlRoot+"/login?next="+i.default.config.urlRoot+window.location.pathname+window.location.hash}function _(t){i.default._internal.challenge={},l.default.getScript(i.default.config.urlRoot+t.scripts.view,function(){var e=t.create;(0,l.default)("#create-chal-entry-div").html(e),(0,u.bindMarkdownEditors)(),l.default.getScript(i.default.config.urlRoot+t.scripts.create,function(){(0,l.default)("#create-chal-entry-div form").submit(function(e){e.preventDefault();var t=(0,l.default)("#create-chal-entry-div form").serializeJSON();i.default.fetch("/api/v1/challenges",{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(e){e.success&&((0,l.default)("#challenge-create-options #challenge_id").val(e.data.id),(0,l.default)("#challenge-create-options").modal())})})})})}function v(a){a.preventDefault();var s=(0,l.default)(a.target).serializeJSON(!0),o={challenge_id:s.challenge_id,content:s.flag||"",type:s.flag_type,data:s.flag_data?s.flag_data:""};Promise.all([new Promise(function(t,e){0!=o.content.length?i.default.fetch("/api/v1/flags",{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(o)}).then(function(e){t(e.json())}):t()}),new Promise(function(e,t){var o=a.target,n={challenge:s.challenge_id,type:"challenge"};(0,l.default)(o.elements.file).val()&&d.default.files.upload(o,n),e()})]).then(function(e){i.default.fetch("/api/v1/challenges/"+s.challenge_id,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({state:s.state})}).then(function(e){return e.json()}).then(function(e){e.success&&setTimeout(function(){window.location=i.default.config.urlRoot+"/admin/challenges/"+s.challenge_id},700)})})}function y(e){var t=(0,l.default)(this).find("option:selected").data("meta");void 0!==t?_(t):(0,l.default)("#create-chal-entry-div").empty()}(0,l.default)(function(){(0,l.default)(".preview-challenge").click(function(e){window.challenge=new Object,i.default._internal.challenge={},l.default.get(i.default.config.urlRoot+"/api/v1/challenges/"+window.CHALLENGE_ID,function(e){var t=i.default._internal.challenge,o=e.data;o.solves=null,l.default.getScript(i.default.config.urlRoot+o.type_data.scripts.view,function(){(0,l.default)("#challenge-window").empty(),(0,l.default)("#challenge-window").append(o.view),(0,l.default)("#challenge-window #challenge-input").addClass("form-control"),(0,l.default)("#challenge-window #challenge-submit").addClass("btn btn-md btn-outline-secondary float-right"),(0,l.default)(".challenge-solves").hide(),(0,l.default)(".nav-tabs a").click(function(e){e.preventDefault(),(0,l.default)(this).tab("show")}),(0,l.default)("#challenge-window").on("hide.bs.modal",function(e){(0,l.default)("#challenge-input").removeClass("wrong"),(0,l.default)("#challenge-input").removeClass("correct"),(0,l.default)("#incorrect-key").slideUp(),(0,l.default)("#correct-key").slideUp(),(0,l.default)("#already-solved").slideUp(),(0,l.default)("#too-fast").slideUp()}),(0,l.default)(".load-hint").on("click",function(e){h((0,l.default)(this).data("hint-id"))}),(0,l.default)("#challenge-submit").click(function(e){e.preventDefault(),(0,l.default)("#challenge-submit").addClass("disabled-button"),(0,l.default)("#challenge-submit").prop("disabled",!0),i.default._internal.challenge.submit(!0).then(g)}),(0,l.default)("#challenge-input").keyup(function(e){13==e.keyCode&&(0,l.default)("#challenge-submit").click()}),t.postRender(),window.location.replace(window.location.href.split("#")[0]+"#preview"),(0,l.default)("#challenge-window").modal()})})}),(0,l.default)(".delete-challenge").click(function(e){(0,a.ezQuery)({title:"Delete Challenge",body:"Are you sure you want to delete {0}".format(""+(0,n.htmlEntities)(window.CHALLENGE_NAME)+""),success:function(){i.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&(window.location=i.default.config.urlRoot+"/admin/challenges")})}})}),(0,l.default)("#challenge-update-container > form").submit(function(e){e.preventDefault();var o=(0,l.default)(e.target).serializeJSON(!0);i.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID+"/flags",{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){function t(){i.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(o)}).then(function(e){return e.json()}).then(function(e){if(e.success){switch((0,l.default)(".challenge-state").text(e.data.state),e.data.state){case"visible":(0,l.default)(".challenge-state").removeClass("badge-danger").addClass("badge-success");break;case"hidden":(0,l.default)(".challenge-state").removeClass("badge-success").addClass("badge-danger")}(0,a.ezToast)({title:"Success",body:"Your challenge has been updated!"})}})}0===e.data.length&&"visible"===o.state?(0,a.ezQuery)({title:"Missing Flags",body:"This challenge does not have any flags meaning it is unsolveable. Are you sure you'd like to update this challenge?",success:t}):t()})}),(0,l.default)("#challenge-create-options form").submit(v),(0,l.default)("#tags-add-input").keyup(r.addTag),(0,l.default)(".delete-tag").click(r.deleteTag),(0,l.default)("#prerequisite-add-form").submit(c.addRequirement),(0,l.default)(".delete-requirement").click(c.deleteRequirement),(0,l.default)("#file-add-form").submit(s.addFile),(0,l.default)(".delete-file").click(s.deleteFile),(0,l.default)("#hint-add-button").click(m.showHintModal),(0,l.default)(".delete-hint").click(m.deleteHint),(0,l.default)(".edit-hint").click(m.showEditHintModal),(0,l.default)("#hint-edit-form").submit(m.editHint),(0,l.default)("#flag-add-button").click(p.addFlagModal),(0,l.default)(".delete-flag").click(p.deleteFlag),(0,l.default)("#flags-create-select").change(p.flagTypeSelect),(0,l.default)(".edit-flag").click(p.editFlagModal),l.default.get(i.default.config.urlRoot+"/api/v1/challenges/types",function(e){(0,l.default)("#create-chals-select").empty();var t=e.data,o=Object.keys(t).length;if(1 -- "),t){var a=t[n],s=(0,l.default)("