From 5d445ecd8640cd19096432efe8bf4f4b6bd2f6d2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 5 Jul 2022 08:45:31 +0200 Subject: [PATCH] COMPATIBILITY: Move all remaining vendor assets into legacy support --- assets/javascripts/legacy/bootbox.js | 717 ++++++++++++++++++++ assets/javascripts/legacy/caret_position.js | 164 +++++ assets/javascripts/wizard-vendor.js | 6 +- 3 files changed, 884 insertions(+), 3 deletions(-) create mode 100644 assets/javascripts/legacy/bootbox.js create mode 100644 assets/javascripts/legacy/caret_position.js diff --git a/assets/javascripts/legacy/bootbox.js b/assets/javascripts/legacy/bootbox.js new file mode 100644 index 00000000..c2520af8 --- /dev/null +++ b/assets/javascripts/legacy/bootbox.js @@ -0,0 +1,717 @@ +// discourse-skip-module + +/** + * bootbox.js v3.2.0 + * + * http://bootboxjs.com/license.txt + */ +var bootbox = + window.bootbox || + (function (document, $) { + /*jshint scripturl:true sub:true */ + + var _locale = "en", + _defaultLocale = "en", + _animate = true, + _backdrop = "static", + _defaultHref = "javascript:;", + _classes = "", + _btnClasses = {}, + _icons = {}, + /* last var should always be the public object we'll return */ + that = {}; + + /** + * public API + */ + that.setLocale = function (locale) { + for (var i in _locales) { + if (i == locale) { + _locale = locale; + return; + } + } + throw new Error("Invalid locale: " + locale); + }; + + that.addLocale = function (locale, translations) { + if (typeof _locales[locale] === "undefined") { + _locales[locale] = {}; + } + for (var str in translations) { + _locales[locale][str] = translations[str]; + } + }; + + that.setIcons = function (icons) { + _icons = icons; + if (typeof _icons !== "object" || _icons === null) { + _icons = {}; + } + }; + + that.setBtnClasses = function (btnClasses) { + _btnClasses = btnClasses; + if (typeof _btnClasses !== "object" || _btnClasses === null) { + _btnClasses = {}; + } + }; + + that.alert = function (/*str, label, cb*/) { + var str = "", + label = _translate("OK"), + cb = null; + + switch (arguments.length) { + case 1: + // no callback, default button label + str = arguments[0]; + break; + case 2: + // callback *or* custom button label dependent on type + str = arguments[0]; + if (typeof arguments[1] == "function") { + cb = arguments[1]; + } else { + label = arguments[1]; + } + break; + case 3: + // callback and custom button label + str = arguments[0]; + label = arguments[1]; + cb = arguments[2]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-3"); + } + + return that.dialog( + str, + { + // only button (ok) + label: label, + icon: _icons.OK, + class: _btnClasses.OK, + callback: cb, + }, + { + // ensure that the escape key works; either invoking the user's + // callback or true to just close the dialog + onEscape: cb || true, + } + ); + }; + + that.confirm = function (/*str, labelCancel, labelOk, cb*/) { + var str = "", + labelCancel = _translate("CANCEL"), + labelOk = _translate("CONFIRM"), + cb = null; + + switch (arguments.length) { + case 1: + str = arguments[0]; + break; + case 2: + str = arguments[0]; + if (typeof arguments[1] == "function") { + cb = arguments[1]; + } else { + labelCancel = arguments[1]; + } + break; + case 3: + str = arguments[0]; + labelCancel = arguments[1]; + if (typeof arguments[2] == "function") { + cb = arguments[2]; + } else { + labelOk = arguments[2]; + } + break; + case 4: + str = arguments[0]; + labelCancel = arguments[1]; + labelOk = arguments[2]; + cb = arguments[3]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-4"); + } + + var cancelCallback = function () { + if (typeof cb === "function") { + return cb(false); + } + }; + + var confirmCallback = function () { + if (typeof cb === "function") { + return cb(true); + } + }; + + return that.dialog( + str, + [ + { + // first button (cancel) + label: labelCancel, + icon: _icons.CANCEL, + class: _btnClasses.CANCEL, + callback: cancelCallback, + }, + { + // second button (confirm) + label: labelOk, + icon: _icons.CONFIRM, + class: _btnClasses.CONFIRM, + callback: confirmCallback, + }, + ], + { + // escape key bindings + onEscape: cancelCallback, + } + ); + }; + + that.prompt = function (/*str, labelCancel, labelOk, cb, defaultVal*/) { + var str = "", + labelCancel = _translate("CANCEL"), + labelOk = _translate("CONFIRM"), + cb = null, + defaultVal = ""; + + switch (arguments.length) { + case 1: + str = arguments[0]; + break; + case 2: + str = arguments[0]; + if (typeof arguments[1] == "function") { + cb = arguments[1]; + } else { + labelCancel = arguments[1]; + } + break; + case 3: + str = arguments[0]; + labelCancel = arguments[1]; + if (typeof arguments[2] == "function") { + cb = arguments[2]; + } else { + labelOk = arguments[2]; + } + break; + case 4: + str = arguments[0]; + labelCancel = arguments[1]; + labelOk = arguments[2]; + cb = arguments[3]; + break; + case 5: + str = arguments[0]; + labelCancel = arguments[1]; + labelOk = arguments[2]; + cb = arguments[3]; + defaultVal = arguments[4]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-5"); + } + + var header = str; + + // let's keep a reference to the form object for later + var form = $("
"); + form.append( + "" + ); + + var cancelCallback = function () { + if (typeof cb === "function") { + // yep, native prompts dismiss with null, whereas native + // confirms dismiss with false... + return cb(null); + } + }; + + var confirmCallback = function () { + if (typeof cb === "function") { + return cb(form.find("input[type=text]").val()); + } + }; + + var div = that.dialog( + form, + [ + { + // first button (cancel) + label: labelCancel, + icon: _icons.CANCEL, + class: _btnClasses.CANCEL, + callback: cancelCallback, + }, + { + // second button (confirm) + label: labelOk, + icon: _icons.CONFIRM, + class: _btnClasses.CONFIRM, + callback: confirmCallback, + }, + ], + { + // prompts need a few extra options + header: header, + // explicitly tell dialog NOT to show the dialog... + show: false, + onEscape: cancelCallback, + } + ); + + // ... the reason the prompt needs to be hidden is because we need + // to bind our own "shown" handler, after creating the modal but + // before any show(n) events are triggered + // @see https://github.com/makeusabrew/bootbox/issues/69 + + div.on("shown", function () { + form.find("input[type=text]").focus(); + + // ensure that submitting the form (e.g. with the enter key) + // replicates the behaviour of a normal prompt() + form.on("submit", function (e) { + e.preventDefault(); + div.find(".btn-primary").click(); + }); + }); + + div.modal("show"); + + return div; + }; + + that.dialog = function (str, handlers, options) { + var buttons = "", + callbacks = []; + + if (!options) { + options = {}; + } + + // check for single object and convert to array if necessary + if (typeof handlers === "undefined") { + handlers = []; + } else if (typeof handlers.length == "undefined") { + handlers = [handlers]; + } + + var i = handlers.length; + while (i--) { + var label = null, + href = null, + _class = "btn-default", + icon = "", + callback = null; + + if ( + typeof handlers[i]["label"] == "undefined" && + typeof handlers[i]["class"] == "undefined" && + typeof handlers[i]["callback"] == "undefined" + ) { + // if we've got nothing we expect, check for condensed format + + var propCount = 0, // condensed will only match if this == 1 + property = null; // save the last property we found + + // be nicer to count the properties without this, but don't think it's possible... + for (var j in handlers[i]) { + property = j; + if (++propCount > 1) { + // forget it, too many properties + break; + } + } + + if (propCount == 1 && typeof handlers[i][j] == "function") { + // matches condensed format of label -> function + handlers[i]["label"] = property; + handlers[i]["callback"] = handlers[i][j]; + } + } + + if (typeof handlers[i]["callback"] == "function") { + callback = handlers[i]["callback"]; + } + + if (handlers[i]["class"]) { + _class = handlers[i]["class"]; + } else if (i == handlers.length - 1 && handlers.length <= 2) { + // always add a primary to the main option in a two-button dialog + _class = "btn-primary"; + } + + // See: https://github.com/makeusabrew/bootbox/pull/114 + // Upgrade to official bootbox release when it gets merged. + if (handlers[i]["link"] !== true) { + _class = "btn " + _class; + } + + if (handlers[i]["label"]) { + label = handlers[i]["label"]; + } else { + label = "Option " + (i + 1); + } + + if (handlers[i]["icon"]) { + icon = handlers[i]["icon"]; + } + + if (handlers[i]["href"]) { + href = handlers[i]["href"]; + } else { + href = _defaultHref; + } + + buttons = + buttons + + "" + + icon + + "" + + label + + ""; + + callbacks[i] = callback; + } + + // @see https://github.com/makeusabrew/bootbox/issues/46#issuecomment-8235302 + // and https://github.com/twitter/bootstrap/issues/4474 + // for an explanation of the inline overflow: hidden + // @see https://github.com/twitter/bootstrap/issues/4854 + // for an explanation of tabIndex=-1 + + var parts = [ + ""); + + var div = $(parts.join("\n")); + + // check whether we should fade in/out + var shouldFade = + typeof options.animate === "undefined" ? _animate : options.animate; + + if (shouldFade) { + div.addClass("fade"); + } + + var optionalClasses = + typeof options.classes === "undefined" ? _classes : options.classes; + if (optionalClasses) { + div.addClass(optionalClasses); + } + + // now we've built up the div properly we can inject the content whether it was a string or a jQuery object + div.find(".modal-body").html(str); + + function onCancel(source) { + // for now source is unused, but it will be in future + var hideModal = null; + if (typeof options.onEscape === "function") { + // @see https://github.com/makeusabrew/bootbox/issues/91 + hideModal = options.onEscape(); + } + + if (hideModal !== false) { + div.modal("hide"); + } + } + + // hook into the modal's keyup trigger to check for the escape key + div.on("keyup.dismiss.modal", function (e) { + // any truthy value passed to onEscape will dismiss the dialog + // as long as the onEscape function (if defined) doesn't prevent it + if (e.which === 27 && options.onEscape !== false) { + onCancel("escape"); + } + }); + + // handle close buttons too + div.on("click", "a.close", function (e) { + e.preventDefault(); + onCancel("close"); + }); + + // well, *if* we have a primary - give the first dom element focus + div.on("shown.bs.modal", function () { + div.find("a.btn-primary:first").focus(); + }); + + div.on("hidden.bs.modal", function () { + div.remove(); + }); + + // wire up button handlers + div.on("click", ".modal-footer a", function (e) { + var self = this; + Ember.run(function () { + var handler = $(self).data("handler"), + cb = callbacks[handler], + hideModal = null; + + // sort of @see https://github.com/makeusabrew/bootbox/pull/68 - heavily adapted + // if we've got a custom href attribute, all bets are off + if ( + typeof handler !== "undefined" && + typeof handlers[handler]["href"] !== "undefined" + ) { + return; + } + + e.preventDefault(); + + if (typeof cb === "function") { + hideModal = cb(e); + } + + // the only way hideModal *will* be false is if a callback exists and + // returns it as a value. in those situations, don't hide the dialog + // @see https://github.com/makeusabrew/bootbox/pull/25 + if (hideModal !== false) { + div.modal("hide"); + } + }); + }); + + // stick the modal right at the bottom of the main body out of the way + (that.$body || $("body")).append(div); + + div.modal({ + // unless explicitly overridden take whatever our default backdrop value is + backdrop: + typeof options.backdrop === "undefined" + ? _backdrop + : options.backdrop, + // ignore bootstrap's keyboard options; we'll handle this ourselves (more fine-grained control) + keyboard: false, + // @ see https://github.com/makeusabrew/bootbox/issues/69 + // we *never* want the modal to be shown before we can bind stuff to it + // this method can also take a 'show' option, but we'll only use that + // later if we need to + show: false, + }); + + // @see https://github.com/makeusabrew/bootbox/issues/64 + // @see https://github.com/makeusabrew/bootbox/issues/60 + // ...caused by... + // @see https://github.com/twitter/bootstrap/issues/4781 + div.on("show", function (e) { + $(document).off("focusin.modal"); + }); + + if (typeof options.show === "undefined" || options.show === true) { + div.modal("show"); + } + + return div; + }; + + /** + * #modal is deprecated in v3; it can still be used but no guarantees are + * made - have never been truly convinced of its merit but perhaps just + * needs a tidyup and some TLC + */ + that.modal = function (/*str, label, options*/) { + var str; + var label; + var options; + + var defaultOptions = { + onEscape: null, + keyboard: true, + backdrop: _backdrop, + }; + + switch (arguments.length) { + case 1: + str = arguments[0]; + break; + case 2: + str = arguments[0]; + if (typeof arguments[1] == "object") { + options = arguments[1]; + } else { + label = arguments[1]; + } + break; + case 3: + str = arguments[0]; + label = arguments[1]; + options = arguments[2]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-3"); + } + + defaultOptions["header"] = label; + + if (typeof options == "object") { + options = $.extend(defaultOptions, options); + } else { + options = defaultOptions; + } + + return that.dialog(str, [], options); + }; + + that.hideAll = function () { + $(".bootbox").modal("hide"); + }; + + that.animate = function (animate) { + _animate = animate; + }; + + that.backdrop = function (backdrop) { + _backdrop = backdrop; + }; + + that.classes = function (classes) { + _classes = classes; + }; + + /** + * private API + */ + + /** + * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are + * unlikely to be required. If this gets too large it can be split out into separate JS files. + */ + var _locales = { + br: { + OK: "OK", + CANCEL: "Cancelar", + CONFIRM: "Sim", + }, + da: { + OK: "OK", + CANCEL: "Annuller", + CONFIRM: "Accepter", + }, + de: { + OK: "OK", + CANCEL: "Abbrechen", + CONFIRM: "Akzeptieren", + }, + en: { + OK: "OK", + CANCEL: "Cancel", + CONFIRM: "OK", + }, + es: { + OK: "OK", + CANCEL: "Cancelar", + CONFIRM: "Aceptar", + }, + fr: { + OK: "OK", + CANCEL: "Annuler", + CONFIRM: "D'accord", + }, + it: { + OK: "OK", + CANCEL: "Annulla", + CONFIRM: "Conferma", + }, + nl: { + OK: "OK", + CANCEL: "Annuleren", + CONFIRM: "Accepteren", + }, + pl: { + OK: "OK", + CANCEL: "Anuluj", + CONFIRM: "Potwierdź", + }, + ru: { + OK: "OK", + CANCEL: "Отмена", + CONFIRM: "Применить", + }, + zh_CN: { + OK: "OK", + CANCEL: "取消", + CONFIRM: "确认", + }, + zh_TW: { + OK: "OK", + CANCEL: "取消", + CONFIRM: "確認", + }, + }; + + function _translate(str, locale) { + // we assume if no target locale is probided then we should take it from current setting + if (typeof locale === "undefined") { + locale = _locale; + } + if (typeof _locales[locale][str] === "string") { + return _locales[locale][str]; + } + + // if we couldn't find a lookup then try and fallback to a default translation + + if (locale != _defaultLocale) { + return _translate(str, _defaultLocale); + } + + // if we can't do anything then bail out with whatever string was passed in - last resort + return str; + } + + return that; + })(document, window.jQuery); + +// @see https://github.com/makeusabrew/bootbox/issues/71 +window.bootbox = bootbox; + +define("bootbox", ["exports"], function (__exports__) { + __exports__.default = window.bootbox; +}); diff --git a/assets/javascripts/legacy/caret_position.js b/assets/javascripts/legacy/caret_position.js new file mode 100644 index 00000000..2ae02794 --- /dev/null +++ b/assets/javascripts/legacy/caret_position.js @@ -0,0 +1,164 @@ +// discourse-skip-module + +// TODO: This code should be moved to lib, it was heavily modified by us over the years, and mostly written by us +// except for the little snippet from StackOverflow +// +// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea +var clone = null; + +$.fn.caret = function(elem) { + var getCaret = function(el) { + if (el.selectionStart) { + return el.selectionStart; + } + return 0; + }; + return getCaret(elem || this[0]); +}; + +/** + This is a jQuery plugin to retrieve the caret position in a textarea + + @module $.fn.caretPosition +**/ +$.fn.caretPosition = function(options) { + var after, + before, + getStyles, + guard, + html, + important, + insertSpaceAfterBefore, + letter, + makeCursor, + p, + pPos, + pos, + span, + styles, + textarea, + val; + if (clone) { + clone.remove(); + } + span = $("#pos span"); + textarea = $(this); + + getStyles = function(el) { + if (el.currentStyle) { + return el.currentStyle; + } else { + return document.defaultView.getComputedStyle(el, ""); + } + }; + + important = function(prop) { + return styles.getPropertyValue(prop); + }; + + styles = getStyles(textarea[0]); + clone = $("

").appendTo("body"); + p = clone.find("p"); + + var isRTL = $("html").hasClass("rtl"); + clone.css({ + border: "1px solid black", + padding: important("padding"), + resize: important("resize"), + "max-height": textarea.height() + "px", + "overflow-y": "auto", + "word-wrap": "break-word", + position: "absolute", + left: isRTL ? "auto" : "-7000px", + right: isRTL ? "-7000px" : "auto" + }); + + p.css({ + margin: 0, + padding: 0, + "word-wrap": "break-word", + "letter-spacing": important("letter-spacing"), + "font-family": important("font-family"), + "font-size": important("font-size"), + "line-height": important("line-height") + }); + + clone.width(textarea.width()); + clone.height(textarea.height()); + + pos = + options && (options.pos || options.pos === 0) + ? options.pos + : $.caret(textarea[0]); + + val = textarea.val().replace("\r", ""); + if (options && options.key) { + val = val.substring(0, pos) + options.key + val.substring(pos); + } + before = pos - 1; + after = pos; + insertSpaceAfterBefore = false; + + // if before and after are \n insert a space + if (val[before] === "\n" && val[after] === "\n") { + insertSpaceAfterBefore = true; + } + + guard = function(v) { + var buf; + buf = v.replace(//g, ">"); + buf = buf.replace(/[ ]/g, "​ ​"); + return buf.replace(/\n/g, "
"); + }; + + makeCursor = function(pos, klass, color) { + var l; + l = val.substring(pos, pos + 1); + if (l === "\n") return "
"; + return ( + "" + + guard(l) + + "" + ); + }; + + html = ""; + + if (before >= 0) { + html += + guard(val.substring(0, pos - 1)) + + makeCursor(before, "before", "#d0ffff"); + if (insertSpaceAfterBefore) { + html += makeCursor(0, "post-before", "#d0ffff"); + } + } + + if (after >= 0) { + html += makeCursor(after, "after", "#ffd0ff"); + if (after - 1 < val.length) { + html += guard(val.substring(after + 1)); + } + } + + p.html(html); + clone.scrollTop(textarea.scrollTop()); + letter = p.find("span:first"); + pos = letter.offset(); + if (letter.hasClass("before")) { + pos.left = pos.left + letter.width(); + } + + pPos = p.offset(); + var position = { + left: pos.left - pPos.left, + top: pos.top - pPos.top - clone.scrollTop() + }; + + clone.remove(); + return position; +}; diff --git a/assets/javascripts/wizard-vendor.js b/assets/javascripts/wizard-vendor.js index 8b1cf56f..a0f25d0c 100644 --- a/assets/javascripts/wizard-vendor.js +++ b/assets/javascripts/wizard-vendor.js @@ -2,12 +2,12 @@ //= require legacy/template_include.js //= require legacy/uppy.js -//= require bootstrap-modal.js -//= require bootbox.js +//= require legacy/bootstrap-modal.js +//= require legacy/bootbox.js //= require legacy/virtual-dom //= require legacy/virtual-dom-amd //= require legacy/itsatrap.js -//= require caret_position.js +//= require legacy/caret_position.js //= require legacy/popper.js //= require legacy/tippy.umd.js //= require legacy/discourse-shims.js