"
+ );
+ }
+
+ // push an empty body into which we'll inject the proper content later
+ parts.push("");
+
+ if (buttons) {
+ parts.push("");
+ }
+
+ parts.push("
");
+
+ 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/bootstrap-modal.js b/assets/javascripts/legacy/bootstrap-modal.js
new file mode 100644
index 00000000..61c76fbd
--- /dev/null
+++ b/assets/javascripts/legacy/bootstrap-modal.js
@@ -0,0 +1,360 @@
+// discourse-skip-module
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.4.1
+ * https://getbootstrap.com/docs/3.4/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // MODAL CLASS DEFINITION
+ // ======================
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$body = $(document.body)
+ this.$element = $(element)
+ this.$dialog = this.$element.find('.modal-dialog')
+ this.$backdrop = null
+ this.isShown = null
+ this.originalBodyPad = null
+ this.scrollbarWidth = 0
+ this.ignoreBackdropClick = false
+ this.fixedContent = '.navbar-fixed-top, .navbar-fixed-bottom'
+
+ if (this.options.remote) {
+ this.$element
+ .find('.modal-content')
+ .load(this.options.remote, $.proxy(function () {
+ this.$element.trigger('loaded.bs.modal')
+ }, this))
+ }
+ }
+
+ Modal.VERSION = '3.4.1'
+
+ Modal.TRANSITION_DURATION = 300
+ Modal.BACKDROP_TRANSITION_DURATION = 150
+
+ Modal.DEFAULTS = {
+ backdrop: true,
+ keyboard: true,
+ show: true
+ }
+
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this.isShown ? this.hide() : this.show(_relatedTarget)
+ }
+
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.checkScrollbar()
+ this.setScrollbar()
+ this.$body.addClass('modal-open')
+
+ this.escape()
+ this.resize()
+
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+ this.$dialog.on('mousedown.dismiss.bs.modal', function () {
+ that.$element.one('mouseup.dismiss.bs.modal', function (e) {
+ if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
+ })
+ })
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(that.$body) // don't move modals dom position
+ }
+
+ that.$element
+ .show()
+ .scrollTop(0)
+
+ that.adjustDialog()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element.addClass('in')
+
+ that.enforceFocus()
+
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+ transition ?
+ that.$dialog // wait for modal to slide in
+ .one('bsTransitionEnd', function () {
+ that.$element.trigger('focus').trigger(e)
+ })
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ that.$element.trigger('focus').trigger(e)
+ })
+ }
+
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
+
+ e = $.Event('hide.bs.modal')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.escape()
+ this.resize()
+
+ $(document).off('focusin.bs.modal')
+
+ this.$element
+ .removeClass('in')
+ .off('click.dismiss.bs.modal')
+ .off('mouseup.dismiss.bs.modal')
+
+ this.$dialog.off('mousedown.dismiss.bs.modal')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ this.hideModal()
+ }
+
+ Modal.prototype.enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', $.proxy(function (e) {
+ if (document !== e.target &&
+ this.$element[0] !== e.target &&
+ !this.$element.has(e.target).length) {
+ this.$element.trigger('focus')
+ }
+ }, this))
+ }
+
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keydown.dismiss.bs.modal')
+ }
+ }
+
+ Modal.prototype.resize = function () {
+ if (this.isShown) {
+ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
+ } else {
+ $(window).off('resize.bs.modal')
+ }
+ }
+
+ Modal.prototype.hideModal = function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.$body.removeClass('modal-open')
+ that.resetAdjustments()
+ that.resetScrollbar()
+ that.$element.trigger('hidden.bs.modal')
+ })
+ }
+
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ Modal.prototype.backdrop = function (callback) {
+ var that = this
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $(document.createElement('div'))
+ .addClass('modal-backdrop ' + animate)
+ .appendTo(this.$body)
+
+ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (this.ignoreBackdropClick) {
+ this.ignoreBackdropClick = false
+ return
+ }
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus()
+ : this.hide()
+ }, this))
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop
+ .one('bsTransitionEnd', callback)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ var callbackRemove = function () {
+ that.removeBackdrop()
+ callback && callback()
+ }
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$backdrop
+ .one('bsTransitionEnd', callbackRemove)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callbackRemove()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+ // these following methods are used to handle overflowing modals
+
+ Modal.prototype.handleUpdate = function () {
+ this.adjustDialog()
+ }
+
+ Modal.prototype.adjustDialog = function () {
+ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
+
+ this.$element.css({
+ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
+ paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
+ })
+ }
+
+ Modal.prototype.resetAdjustments = function () {
+ this.$element.css({
+ paddingLeft: '',
+ paddingRight: ''
+ })
+ }
+
+ Modal.prototype.checkScrollbar = function () {
+ var fullWindowWidth = window.innerWidth
+ if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
+ var documentElementRect = document.documentElement.getBoundingClientRect()
+ fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
+ }
+ this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
+ this.scrollbarWidth = this.measureScrollbar()
+ }
+
+ Modal.prototype.setScrollbar = function () {
+ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+ this.originalBodyPad = document.body.style.paddingRight || ''
+ var scrollbarWidth = this.scrollbarWidth
+ if (this.bodyIsOverflowing) {
+ this.$body.css('padding-right', bodyPad + scrollbarWidth)
+ $(this.fixedContent).each(function (index, element) {
+ var actualPadding = element.style.paddingRight
+ var calculatedPadding = $(element).css('padding-right')
+ $(element)
+ .data('padding-right', actualPadding)
+ .css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px')
+ })
+ }
+ }
+
+ Modal.prototype.resetScrollbar = function () {
+ this.$body.css('padding-right', this.originalBodyPad)
+ $(this.fixedContent).each(function (index, element) {
+ var padding = $(element).data('padding-right')
+ $(element).removeData('padding-right')
+ element.style.paddingRight = padding ? padding : ''
+ })
+ }
+
+ Modal.prototype.measureScrollbar = function () { // thx walsh
+ var scrollDiv = document.createElement('div')
+ scrollDiv.className = 'modal-scrollbar-measure'
+ this.$body.append(scrollDiv)
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ this.$body[0].removeChild(scrollDiv)
+ return scrollbarWidth
+ }
+
+
+ // MODAL PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+
+ var old = $.fn.modal
+
+ $.fn.modal = Plugin
+ $.fn.modal.Constructor = Modal
+
+
+ // MODAL NO CONFLICT
+ // =================
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ // MODAL DATA-API
+ // ==============
+
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ var href = $this.attr('href')
+ var target = $this.attr('data-target') ||
+ (href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
+
+ var $target = $(document).find(target)
+ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ if ($this.is('a')) e.preventDefault()
+
+ $target.one('show.bs.modal', function (showEvent) {
+ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
+ $target.one('hidden.bs.modal', function () {
+ $this.is(':visible') && $this.trigger('focus')
+ })
+ })
+ Plugin.call($target, option, this)
+ })
+
+}(jQuery);
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/legacy/discourse-loader.js b/assets/javascripts/legacy/discourse-loader.js
new file mode 100644
index 00000000..ee9a2a07
--- /dev/null
+++ b/assets/javascripts/legacy/discourse-loader.js
@@ -0,0 +1,435 @@
+// discourse-skip-module
+
+var define, requirejs;
+
+(function () {
+ let JS_MODULES = {};
+ let ALIASES = {
+ "ember-addons/ember-computed-decorators":
+ "discourse-common/utils/decorators",
+ "discourse/lib/raw-templates": "discourse/plugins/discourse-custom-wizard/legacy/raw-templates",
+ "discourse-common/lib/raw-templates": "discourse/plugins/discourse-custom-wizard/legacy/raw-templates",
+ "preload-store": "discourse/lib/preload-store",
+ "fixtures/user_fixtures": "discourse/tests/fixtures/user-fixtures",
+ };
+ let ALIAS_PREPEND = {
+ fixtures: "discourse/tests/",
+ helpers: "discourse/tests/",
+ };
+
+ // In future versions of ember we don't need this
+ if (typeof Ember !== "undefined") {
+ JS_MODULES = {
+ jquery: { default: $ },
+ "@ember/array": {
+ default: Ember.Array,
+ A: Ember.A,
+ isArray: Ember.isArray,
+ },
+ "@ember/array/proxy": {
+ default: Ember.ArrayProxy,
+ },
+ "@ember/component": {
+ default: Ember.Component,
+ },
+ "@ember/controller": {
+ default: Ember.Controller,
+ inject: Ember.inject.controller,
+ },
+ "@ember/debug": {
+ assert: Ember.assert,
+ runInDebug: Ember.runInDebug,
+ warn: Ember.warn,
+ },
+ "@ember/object": {
+ action: Ember._action,
+ default: Ember.Object,
+ get: Ember.get,
+ getProperties: Ember.getProperties,
+ set: Ember.set,
+ setProperties: Ember.setProperties,
+ computed: Ember.computed,
+ defineProperty: Ember.defineProperty,
+ observer: Ember.observer,
+ },
+ "@ember/object/computed": {
+ alias: Ember.computed.alias,
+ and: Ember.computed.and,
+ bool: Ember.computed.bool,
+ collect: Ember.computed.collect,
+ deprecatingAlias: Ember.computed.deprecatingAlias,
+ empty: Ember.computed.empty,
+ equal: Ember.computed.equal,
+ filter: Ember.computed.filter,
+ filterBy: Ember.computed.filterBy,
+ gt: Ember.computed.gt,
+ gte: Ember.computed.gte,
+ intersect: Ember.computed.intersect,
+ lt: Ember.computed.lt,
+ lte: Ember.computed.lte,
+ map: Ember.computed.map,
+ mapBy: Ember.computed.mapBy,
+ match: Ember.computed.match,
+ max: Ember.computed.max,
+ min: Ember.computed.min,
+ none: Ember.computed.none,
+ not: Ember.computed.not,
+ notEmpty: Ember.computed.notEmpty,
+ oneWay: Ember.computed.oneWay,
+ or: Ember.computed.or,
+ readOnly: Ember.computed.readOnly,
+ reads: Ember.computed.reads,
+ setDiff: Ember.computed.setDiff,
+ sort: Ember.computed.sort,
+ sum: Ember.computed.sum,
+ union: Ember.computed.union,
+ uniq: Ember.computed.uniq,
+ uniqBy: Ember.computed.uniqBy,
+ },
+ "@ember/object/mixin": { default: Ember.Mixin },
+ "@ember/object/proxy": { default: Ember.ObjectProxy },
+ "@ember/object/promise-proxy-mixin": { default: Ember.PromiseProxyMixin },
+ "@ember/object/evented": {
+ default: Ember.Evented,
+ on: Ember.on,
+ },
+ "@ember/routing/route": { default: Ember.Route },
+ "@ember/routing/router": { default: Ember.Router },
+ "@ember/runloop": {
+ bind: Ember.run.bind,
+ cancel: Ember.run.cancel,
+ debounce: Ember.testing ? Ember.run : Ember.run.debounce,
+ later: Ember.run.later,
+ next: Ember.run.next,
+ once: Ember.run.once,
+ run: Ember.run,
+ schedule: Ember.run.schedule,
+ scheduleOnce: Ember.run.scheduleOnce,
+ throttle: Ember.run.throttle,
+ },
+ "@ember/service": {
+ default: Ember.Service,
+ inject: Ember.inject.service,
+ },
+ "@ember/utils": {
+ isBlank: Ember.isBlank,
+ isEmpty: Ember.isEmpty,
+ isNone: Ember.isNone,
+ isPresent: Ember.isPresent,
+ },
+ rsvp: {
+ asap: Ember.RSVP.asap,
+ all: Ember.RSVP.all,
+ allSettled: Ember.RSVP.allSettled,
+ race: Ember.RSVP.race,
+ hash: Ember.RSVP.hash,
+ hashSettled: Ember.RSVP.hashSettled,
+ rethrow: Ember.RSVP.rethrow,
+ defer: Ember.RSVP.defer,
+ denodeify: Ember.RSVP.denodeify,
+ resolve: Ember.RSVP.resolve,
+ reject: Ember.RSVP.reject,
+ map: Ember.RSVP.map,
+ filter: Ember.RSVP.filter,
+ default: Ember.RSVP,
+ Promise: Ember.RSVP.Promise,
+ EventTarget: Ember.RSVP.EventTarget,
+ },
+ "@ember/string": {
+ w: Ember.String.w,
+ dasherize: Ember.String.dasherize,
+ decamelize: Ember.String.decamelize,
+ camelize: Ember.String.camelize,
+ classify: Ember.String.classify,
+ underscore: Ember.String.underscore,
+ capitalize: Ember.String.capitalize,
+ },
+ "@ember/template": {
+ htmlSafe: Ember.String.htmlSafe,
+ },
+ "@ember/application": {
+ default: Ember.Application,
+ setOwner: Ember.setOwner,
+ getOwner: Ember.getOwner,
+ },
+ "@ember/component/helper": {
+ default: Ember.Helper,
+ },
+ "@ember/component/text-field": {
+ default: Ember.TextField,
+ },
+ "@ember/component/text-area": {
+ default: Ember.TextArea,
+ },
+ "@ember/error": {
+ default: Ember.error,
+ },
+ "@ember/object/internals": {
+ guidFor: Ember.guidFor,
+ },
+ "@ember/test": {
+ registerWaiter: Ember.Test && Ember.Test.registerWaiter,
+ unregisterWaiter: Ember.Test && Ember.Test.unregisterWaiter,
+ },
+ I18n: {
+ // eslint-disable-next-line
+ default: I18n,
+ }
+ };
+ }
+
+ let _isArray;
+ if (!Array.isArray) {
+ _isArray = function (x) {
+ return Object.prototype.toString.call(x) === "[object Array]";
+ };
+ } else {
+ _isArray = Array.isArray;
+ }
+
+ let registry = {};
+ let seen = {};
+ let FAILED = false;
+
+ let uuid = 0;
+
+ function tryFinally(tryable, finalizer) {
+ try {
+ return tryable();
+ } finally {
+ finalizer();
+ }
+ }
+
+ function unsupportedModule(length) {
+ throw new Error(
+ "an unsupported module was defined, expected `define(name, deps, module)` instead got: `" +
+ length +
+ "` arguments to define`"
+ );
+ }
+
+ function deprecatedModule(depricated, useInstead) {
+ let warning = "[DEPRECATION] `" + depricated + "` is deprecated.";
+ if (useInstead) {
+ warning += " Please use `" + useInstead + "` instead.";
+ }
+ // eslint-disable-next-line no-console
+ console.warn(warning);
+ }
+
+ let defaultDeps = ["require", "exports", "module"];
+
+ function Module(name, deps, callback, exports) {
+ this.id = uuid++;
+ this.name = name;
+ this.deps = !deps.length && callback.length ? defaultDeps : deps;
+ this.exports = exports || {};
+ this.callback = callback;
+ this.state = undefined;
+ this._require = undefined;
+ }
+
+ Module.prototype.makeRequire = function () {
+ let name = transformForAliases(this.name);
+
+ return (
+ this._require ||
+ (this._require = function (dep) {
+ return requirejs(resolve(dep, name));
+ })
+ );
+ };
+
+ define = function (name, deps, callback) {
+ if (arguments.length < 2) {
+ unsupportedModule(arguments.length);
+ }
+
+ if (!_isArray(deps)) {
+ callback = deps;
+ deps = [];
+ }
+
+ registry[name] = new Module(name, deps, callback);
+ };
+
+ // we don't support all of AMD
+ // define.amd = {};
+ // we will support petals...
+ define.petal = {};
+
+ function Alias(path) {
+ this.name = path;
+ }
+
+ define.alias = function (path) {
+ return new Alias(path);
+ };
+
+ function reify(mod, name, rseen) {
+ let deps = mod.deps;
+ let length = deps.length;
+ let reified = new Array(length);
+ let dep;
+ // TODO: new Module
+ // TODO: seen refactor
+ let module = {};
+
+ for (let i = 0, l = length; i < l; i++) {
+ dep = deps[i];
+ if (dep === "exports") {
+ module.exports = reified[i] = rseen;
+ } else if (dep === "require") {
+ reified[i] = mod.makeRequire();
+ } else if (dep === "module") {
+ mod.exports = rseen;
+ module = reified[i] = mod;
+ } else {
+ reified[i] = requireFrom(resolve(dep, name), name);
+ }
+ }
+
+ return {
+ deps: reified,
+ module,
+ };
+ }
+
+ function requireFrom(name, origin) {
+ name = transformForAliases(name);
+
+ if (name === "discourse") {
+ // eslint-disable-next-line no-console
+ console.log(
+ "discourse has been moved to `discourse/app` - please update your code"
+ );
+ name = "discourse/app";
+ }
+
+ if (name === "discourse/models/input-validation") {
+ // eslint-disable-next-line no-console
+ console.log(
+ "input-validation has been removed and should be replaced with `@ember/object`"
+ );
+ name = "@ember/object";
+ }
+
+ let mod = JS_MODULES[name] || registry[name];
+ if (!mod) {
+ throw new Error(
+ "Could not find module `" + name + "` imported from `" + origin + "`"
+ );
+ }
+ return requirejs(name);
+ }
+
+ function missingModule(name) {
+ throw new Error("Could not find module " + name);
+ }
+
+ function transformForAliases(name) {
+ let alias = ALIASES[name];
+ if (!alias) {
+ let segment = name.split("/")[0];
+ let prepend = ALIAS_PREPEND[segment];
+ if (!prepend) {
+ return name;
+ }
+ alias = prepend + name;
+ }
+ deprecatedModule(name, alias);
+ return alias;
+ }
+
+ requirejs = require = function (name) {
+ name = transformForAliases(name);
+ if (JS_MODULES[name]) {
+ return JS_MODULES[name];
+ }
+
+ let mod = registry[name];
+
+ if (mod && mod.callback instanceof Alias) {
+ mod = registry[mod.callback.name];
+ }
+
+ if (!mod) {
+ missingModule(name);
+ }
+
+ if (mod.state !== FAILED && seen.hasOwnProperty(name)) {
+ return seen[name];
+ }
+
+ let reified;
+ let module;
+ let loaded = false;
+
+ seen[name] = {}; // placeholder for run-time cycles
+
+ tryFinally(
+ function () {
+ reified = reify(mod, name, seen[name]);
+ module = mod.callback.apply(this, reified.deps);
+ loaded = true;
+ },
+ function () {
+ if (!loaded) {
+ mod.state = FAILED;
+ }
+ }
+ );
+
+ let obj;
+ if (module === undefined && reified.module.exports) {
+ obj = reified.module.exports;
+ } else {
+ obj = seen[name] = module;
+ }
+
+ if (
+ obj !== null &&
+ (typeof obj === "object" || typeof obj === "function") &&
+ obj["default"] === undefined
+ ) {
+ obj["default"] = obj;
+ }
+
+ return (seen[name] = obj);
+ };
+ window.requireModule = requirejs;
+
+ function resolve(child, name) {
+ if (child.charAt(0) !== ".") {
+ return child;
+ }
+
+ let parts = child.split("/");
+ let nameParts = name.split("/");
+ let parentBase = nameParts.slice(0, -1);
+
+ for (let i = 0, l = parts.length; i < l; i++) {
+ let part = parts[i];
+
+ if (part === "..") {
+ if (parentBase.length === 0) {
+ throw new Error("Cannot access parent module of root");
+ }
+ parentBase.pop();
+ } else if (part === ".") {
+ continue;
+ } else {
+ parentBase.push(part);
+ }
+ }
+
+ return parentBase.join("/");
+ }
+
+ requirejs.entries = requirejs._eak_seen = registry;
+ requirejs.clear = function () {
+ requirejs.entries = requirejs._eak_seen = registry = {};
+ seen = {};
+ };
+})();
diff --git a/assets/javascripts/legacy/discourse-shims.js b/assets/javascripts/legacy/discourse-shims.js
new file mode 100644
index 00000000..a6b25771
--- /dev/null
+++ b/assets/javascripts/legacy/discourse-shims.js
@@ -0,0 +1,69 @@
+// discourse-skip-module
+
+define("message-bus-client", ["exports"], function (__exports__) {
+ __exports__.default = window.MessageBus;
+});
+
+define("ember-buffered-proxy/proxy", ["exports"], function (__exports__) {
+ __exports__.default = window.BufferedProxy;
+});
+
+define("bootbox", ["exports"], function (__exports__) {
+ __exports__.default = window.bootbox;
+});
+
+define("xss", ["exports"], function (__exports__) {
+ __exports__.default = window.filterXSS;
+});
+
+define("@discourse/itsatrap", ["exports"], function (__exports__) {
+ __exports__.default = window.ItsATrap;
+});
+
+define("@popperjs/core", ["exports"], function (__exports__) {
+ __exports__.default = window.Popper;
+ __exports__.createPopper = window.Popper.createPopper;
+ __exports__.defaultModifiers = window.Popper.defaultModifiers;
+ __exports__.popperGenerator = window.Popper.popperGenerator;
+});
+
+define("tippy.js", ["exports"], function (__exports__) {
+ __exports__.default = window.tippy;
+});
+
+define("@uppy/core", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.Core;
+ __exports__.BasePlugin = window.Uppy.Core.BasePlugin;
+});
+
+define("@uppy/aws-s3", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.AwsS3;
+});
+
+define("@uppy/aws-s3-multipart", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.AwsS3Multipart;
+});
+
+define("@uppy/xhr-upload", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.XHRUpload;
+});
+
+define("@uppy/drop-target", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.DropTarget;
+});
+
+define("@uppy/utils/lib/delay", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.Utils.delay;
+});
+
+define("@uppy/utils/lib/EventTracker", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.Utils.EventTracker;
+});
+
+define("@uppy/utils/lib/AbortController", ["exports"], function (__exports__) {
+ __exports__.AbortController =
+ window.Uppy.Utils.AbortControllerLib.AbortController;
+ __exports__.AbortSignal = window.Uppy.Utils.AbortControllerLib.AbortSignal;
+ __exports__.createAbortError =
+ window.Uppy.Utils.AbortControllerLib.createAbortError;
+});
diff --git a/assets/javascripts/legacy/ember_include.js.erb b/assets/javascripts/legacy/ember_include.js.erb
new file mode 100644
index 00000000..0d6ddf43
--- /dev/null
+++ b/assets/javascripts/legacy/ember_include.js.erb
@@ -0,0 +1,9 @@
+// discourse-skip-module
+
+<%
+ if @force_ember_debug || Rails.env.development? || Rails.env.test?
+ require_asset("ember.debug.js")
+ else
+ require_asset("ember.prod.js")
+ end
+%>
diff --git a/assets/javascripts/legacy/env.js b/assets/javascripts/legacy/env.js
new file mode 100644
index 00000000..0ae0e456
--- /dev/null
+++ b/assets/javascripts/legacy/env.js
@@ -0,0 +1,5 @@
+// discourse-skip-module
+
+window.ENV = {};
+window.EmberENV = window.EmberENV || {};
+window.EmberENV.FORCE_JQUERY = true;
diff --git a/assets/javascripts/legacy/handlebars.runtime.js b/assets/javascripts/legacy/handlebars.runtime.js
new file mode 100644
index 00000000..145aa7c1
--- /dev/null
+++ b/assets/javascripts/legacy/handlebars.runtime.js
@@ -0,0 +1,1802 @@
+// discourse-skip-module
+
+/**!
+
+ @license
+ handlebars v4.7.7
+
+Copyright (C) 2011-2019 by Yehuda Katz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+(function webpackUniversalModuleDefinition(root, factory) {
+ if(typeof exports === 'object' && typeof module === 'object')
+ {module.exports = factory();}
+ else if(typeof define === 'function' && define.amd)
+ {define([], factory);}
+ else if(typeof exports === 'object')
+ {exports["Handlebars"] = factory();}
+ else
+ {root["Handlebars"] = factory();}
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ let 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)
+/******/ let module = installedModules[moduleId] = {
+/******/ exports: {},
+/******/ id: moduleId,
+/******/ loaded: false
+/******/ };
+
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+
+/******/ // Flag the module as loaded
+/******/ module.loaded = 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;
+
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireWildcard = __webpack_require__(1)['default'];
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+
+ let _handlebarsBase = __webpack_require__(3);
+
+ let base = _interopRequireWildcard(_handlebarsBase);
+
+ // Each of these augment the Handlebars object. No need to setup here.
+ // (This is done to easily share code between commonjs and browse envs)
+
+ let _handlebarsSafeString = __webpack_require__(36);
+
+ let _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString);
+
+ let _handlebarsException = __webpack_require__(5);
+
+ let _handlebarsException2 = _interopRequireDefault(_handlebarsException);
+
+ let _handlebarsUtils = __webpack_require__(4);
+
+ let Utils = _interopRequireWildcard(_handlebarsUtils);
+
+ let _handlebarsRuntime = __webpack_require__(37);
+
+ let runtime = _interopRequireWildcard(_handlebarsRuntime);
+
+ let _handlebarsNoConflict = __webpack_require__(43);
+
+ let _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
+
+ // For compatibility and usage outside of module systems, make the Handlebars object a namespace
+ function create() {
+ let hb = new base.HandlebarsEnvironment();
+
+ Utils.extend(hb, base);
+ hb.SafeString = _handlebarsSafeString2['default'];
+ hb.Exception = _handlebarsException2['default'];
+ hb.Utils = Utils;
+ hb.escapeExpression = Utils.escapeExpression;
+
+ hb.VM = runtime;
+ hb.template = function (spec) {
+ return runtime.template(spec, hb);
+ };
+
+ return hb;
+ }
+
+ let inst = create();
+ inst.create = create;
+
+ _handlebarsNoConflict2['default'](inst);
+
+ inst['default'] = inst;
+
+ exports['default'] = inst;
+ module.exports = exports['default'];
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+ "use strict";
+
+ exports["default"] = function (obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ } else {
+ let newObj = {};
+
+ if (obj != null) {
+ for (let key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {newObj[key] = obj[key];}
+ }
+ }
+
+ newObj["default"] = obj;
+ return newObj;
+ }
+ };
+
+ exports.__esModule = true;
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports) {
+
+ "use strict";
+
+ exports["default"] = function (obj) {
+ return obj && obj.__esModule ? obj : {
+ "default": obj
+ };
+ };
+
+ exports.__esModule = true;
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+ exports.HandlebarsEnvironment = HandlebarsEnvironment;
+
+ let _utils = __webpack_require__(4);
+
+ let _exception = __webpack_require__(5);
+
+ let _exception2 = _interopRequireDefault(_exception);
+
+ let _helpers = __webpack_require__(9);
+
+ let _decorators = __webpack_require__(29);
+
+ let _logger = __webpack_require__(31);
+
+ let _logger2 = _interopRequireDefault(_logger);
+
+ let _internalProtoAccess = __webpack_require__(32);
+
+ let VERSION = '4.7.7';
+ exports.VERSION = VERSION;
+ let COMPILER_REVISION = 8;
+ exports.COMPILER_REVISION = COMPILER_REVISION;
+ let LAST_COMPATIBLE_COMPILER_REVISION = 7;
+
+ exports.LAST_COMPATIBLE_COMPILER_REVISION = LAST_COMPATIBLE_COMPILER_REVISION;
+ let REVISION_CHANGES = {
+ 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
+ 2: '== 1.0.0-rc.3',
+ 3: '== 1.0.0-rc.4',
+ 4: '== 1.x.x',
+ 5: '== 2.0.0-alpha.x',
+ 6: '>= 2.0.0-beta.1',
+ 7: '>= 4.0.0 <4.3.0',
+ 8: '>= 4.3.0'
+ };
+
+ exports.REVISION_CHANGES = REVISION_CHANGES;
+ let objectType = '[object Object]';
+
+ function HandlebarsEnvironment(helpers, partials, decorators) {
+ this.helpers = helpers || {};
+ this.partials = partials || {};
+ this.decorators = decorators || {};
+
+ _helpers.registerDefaultHelpers(this);
+ _decorators.registerDefaultDecorators(this);
+ }
+
+ HandlebarsEnvironment.prototype = {
+ constructor: HandlebarsEnvironment,
+
+ logger: _logger2['default'],
+ log: _logger2['default'].log,
+
+ registerHelper: function registerHelper(name, fn) {
+ if (_utils.toString.call(name) === objectType) {
+ if (fn) {
+ throw new _exception2['default']('Arg not supported with multiple helpers');
+ }
+ _utils.extend(this.helpers, name);
+ } else {
+ this.helpers[name] = fn;
+ }
+ },
+ unregisterHelper: function unregisterHelper(name) {
+ delete this.helpers[name];
+ },
+
+ registerPartial: function registerPartial(name, partial) {
+ if (_utils.toString.call(name) === objectType) {
+ _utils.extend(this.partials, name);
+ } else {
+ if (typeof partial === 'undefined') {
+ throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined');
+ }
+ this.partials[name] = partial;
+ }
+ },
+ unregisterPartial: function unregisterPartial(name) {
+ delete this.partials[name];
+ },
+
+ registerDecorator: function registerDecorator(name, fn) {
+ if (_utils.toString.call(name) === objectType) {
+ if (fn) {
+ throw new _exception2['default']('Arg not supported with multiple decorators');
+ }
+ _utils.extend(this.decorators, name);
+ } else {
+ this.decorators[name] = fn;
+ }
+ },
+ unregisterDecorator: function unregisterDecorator(name) {
+ delete this.decorators[name];
+ },
+ /**
+ * Reset the memory of illegal property accesses that have already been logged.
+ * @deprecated should only be used in handlebars test-cases
+ */
+ resetLoggedPropertyAccesses: function resetLoggedPropertyAccesses() {
+ _internalProtoAccess.resetLoggedProperties();
+ }
+ };
+
+ let log = _logger2['default'].log;
+
+ exports.log = log;
+ exports.createFrame = _utils.createFrame;
+ exports.logger = _logger2['default'];
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ exports.extend = extend;
+ exports.indexOf = indexOf;
+ exports.escapeExpression = escapeExpression;
+ exports.isEmpty = isEmpty;
+ exports.createFrame = createFrame;
+ exports.blockParams = blockParams;
+ exports.appendContextPath = appendContextPath;
+ let escape = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '`': '`',
+ '=': '='
+ };
+
+ let badChars = /[&<>"'`=]/g,
+ possible = /[&<>"'`=]/;
+
+ function escapeChar(chr) {
+ return escape[chr];
+ }
+
+ function extend(obj /* , ...source */) {
+ for (let i = 1; i < arguments.length; i++) {
+ for (let key in arguments[i]) {
+ if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
+ obj[key] = arguments[i][key];
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ let toString = Object.prototype.toString;
+
+ exports.toString = toString;
+ // Sourced from lodash
+ // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
+ /* eslint-disable func-style */
+ let isFunction = function isFunction(value) {
+ return typeof value === 'function';
+ };
+ // fallback for older versions of Chrome and Safari
+ /* istanbul ignore next */
+ if (isFunction(/x/)) {
+ exports.isFunction = isFunction = function (value) {
+ return typeof value === 'function' && toString.call(value) === '[object Function]';
+ };
+ }
+ exports.isFunction = isFunction;
+
+ /* eslint-enable func-style */
+
+ /* istanbul ignore next */
+ let isArray = Array.isArray || function (value) {
+ return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false;
+ };
+
+ exports.isArray = isArray;
+ // Older IE versions do not directly support indexOf so we must implement our own, sadly.
+
+ function indexOf(array, value) {
+ for (let i = 0, len = array.length; i < len; i++) {
+ if (array[i] === value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function escapeExpression(string) {
+ if (typeof string !== 'string') {
+ // don't escape SafeStrings, since they're already safe
+ if (string && string.toHTML) {
+ return string.toHTML();
+ } else if (string == null) {
+ return '';
+ } else if (!string) {
+ return string + '';
+ }
+
+ // Force a string conversion as this will be done by the append regardless and
+ // the regex test will do this transparently behind the scenes, causing issues if
+ // an object's to string has escaped characters in it.
+ string = '' + string;
+ }
+
+ if (!possible.test(string)) {
+ return string;
+ }
+ return string.replace(badChars, escapeChar);
+ }
+
+ function isEmpty(value) {
+ if (!value && value !== 0) {
+ return true;
+ } else if (isArray(value) && value.length === 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function createFrame(object) {
+ let frame = extend({}, object);
+ frame._parent = object;
+ return frame;
+ }
+
+ function blockParams(params, ids) {
+ params.path = ids;
+ return params;
+ }
+
+ function appendContextPath(contextPath, id) {
+ return (contextPath ? contextPath + '.' : '') + id;
+ }
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _Object$defineProperty = __webpack_require__(6)['default'];
+
+ exports.__esModule = true;
+ let errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack'];
+
+ function Exception(message, node) {
+ let loc = node && node.loc,
+ line = undefined,
+ endLineNumber = undefined,
+ column = undefined,
+ endColumn = undefined;
+
+ if (loc) {
+ line = loc.start.line;
+ endLineNumber = loc.end.line;
+ column = loc.start.column;
+ endColumn = loc.end.column;
+
+ message += ' - ' + line + ':' + column;
+ }
+
+ let tmp = Error.prototype.constructor.call(this, message);
+
+ // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
+ for (let idx = 0; idx < errorProps.length; idx++) {
+ this[errorProps[idx]] = tmp[errorProps[idx]];
+ }
+
+ /* istanbul ignore else */
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, Exception);
+ }
+
+ try {
+ if (loc) {
+ this.lineNumber = line;
+ this.endLineNumber = endLineNumber;
+
+ // Work around issue under safari where we can't directly set the column value
+ /* istanbul ignore next */
+ if (_Object$defineProperty) {
+ Object.defineProperty(this, 'column', {
+ value: column,
+ enumerable: true
+ });
+ Object.defineProperty(this, 'endColumn', {
+ value: endColumn,
+ enumerable: true
+ });
+ } else {
+ this.column = column;
+ this.endColumn = endColumn;
+ }
+ }
+ } catch (nop) {
+ /* Ignore if the browser is very particular */
+ }
+ }
+
+ Exception.prototype = new Error();
+
+ exports['default'] = Exception;
+ module.exports = exports['default'];
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ module.exports = { "default": __webpack_require__(7), __esModule: true };
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ let $ = __webpack_require__(8);
+ module.exports = function defineProperty(it, key, desc){
+ return $.setDesc(it, key, desc);
+ };
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+
+ let $Object = Object;
+ module.exports = {
+ create: $Object.create,
+ getProto: $Object.getPrototypeOf,
+ isEnum: {}.propertyIsEnumerable,
+ getDesc: $Object.getOwnPropertyDescriptor,
+ setDesc: $Object.defineProperty,
+ setDescs: $Object.defineProperties,
+ getKeys: $Object.keys,
+ getNames: $Object.getOwnPropertyNames,
+ getSymbols: $Object.getOwnPropertySymbols,
+ each: [].forEach
+ };
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+ exports.registerDefaultHelpers = registerDefaultHelpers;
+ exports.moveHelperToHooks = moveHelperToHooks;
+
+ let _helpersBlockHelperMissing = __webpack_require__(10);
+
+ let _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing);
+
+ let _helpersEach = __webpack_require__(11);
+
+ let _helpersEach2 = _interopRequireDefault(_helpersEach);
+
+ let _helpersHelperMissing = __webpack_require__(24);
+
+ let _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing);
+
+ let _helpersIf = __webpack_require__(25);
+
+ let _helpersIf2 = _interopRequireDefault(_helpersIf);
+
+ let _helpersLog = __webpack_require__(26);
+
+ let _helpersLog2 = _interopRequireDefault(_helpersLog);
+
+ let _helpersLookup = __webpack_require__(27);
+
+ let _helpersLookup2 = _interopRequireDefault(_helpersLookup);
+
+ let _helpersWith = __webpack_require__(28);
+
+ let _helpersWith2 = _interopRequireDefault(_helpersWith);
+
+ function registerDefaultHelpers(instance) {
+ _helpersBlockHelperMissing2['default'](instance);
+ _helpersEach2['default'](instance);
+ _helpersHelperMissing2['default'](instance);
+ _helpersIf2['default'](instance);
+ _helpersLog2['default'](instance);
+ _helpersLookup2['default'](instance);
+ _helpersWith2['default'](instance);
+ }
+
+ function moveHelperToHooks(instance, helperName, keepHelper) {
+ if (instance.helpers[helperName]) {
+ instance.hooks[helperName] = instance.helpers[helperName];
+ if (!keepHelper) {
+ delete instance.helpers[helperName];
+ }
+ }
+ }
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ let _utils = __webpack_require__(4);
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('blockHelperMissing', function (context, options) {
+ let inverse = options.inverse,
+ fn = options.fn;
+
+ if (context === true) {
+ return fn(this);
+ } else if (context === false || context == null) {
+ return inverse(this);
+ } else if (_utils.isArray(context)) {
+ if (context.length > 0) {
+ if (options.ids) {
+ options.ids = [options.name];
+ }
+
+ return instance.helpers.each(context, options);
+ } else {
+ return inverse(this);
+ }
+ } else {
+ if (options.data && options.ids) {
+ let data = _utils.createFrame(options.data);
+ data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name);
+ options = { data };
+ }
+
+ return fn(context, options);
+ }
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(global) {'use strict';
+
+ let _Object$keys = __webpack_require__(12)['default'];
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+
+ let _utils = __webpack_require__(4);
+
+ let _exception = __webpack_require__(5);
+
+ let _exception2 = _interopRequireDefault(_exception);
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('each', function (context, options) {
+ if (!options) {
+ throw new _exception2['default']('Must pass iterator to #each');
+ }
+
+ let fn = options.fn,
+ inverse = options.inverse,
+ i = 0,
+ ret = '',
+ data = undefined,
+ contextPath = undefined;
+
+ if (options.data && options.ids) {
+ contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
+ }
+
+ if (_utils.isFunction(context)) {
+ context = context.call(this);
+ }
+
+ if (options.data) {
+ data = _utils.createFrame(options.data);
+ }
+
+ function execIteration(field, index, last) {
+ if (data) {
+ data.key = field;
+ data.index = index;
+ data.first = index === 0;
+ data.last = !!last;
+
+ if (contextPath) {
+ data.contextPath = contextPath + field;
+ }
+ }
+
+ ret = ret + fn(context[field], {
+ data,
+ blockParams: _utils.blockParams([context[field], field], [contextPath + field, null])
+ });
+ }
+
+ if (context && typeof context === 'object') {
+ if (_utils.isArray(context)) {
+ for (var j = context.length; i < j; i++) {
+ if (i in context) {
+ execIteration(i, i, i === context.length - 1);
+ }
+ }
+ } else if (global.Symbol && context[global.Symbol.iterator]) {
+ let newContext = [];
+ let iterator = context[global.Symbol.iterator]();
+ for (let it = iterator.next(); !it.done; it = iterator.next()) {
+ newContext.push(it.value);
+ }
+ context = newContext;
+ for (var j = context.length; i < j; i++) {
+ execIteration(i, i, i === context.length - 1);
+ }
+ } else {
+ (function () {
+ let priorKey = undefined;
+
+ _Object$keys(context).forEach(function (key) {
+ // We're running the iterations one step out of sync so we can detect
+ // the last iteration without have to scan the object twice and create
+ // an itermediate keys array.
+ if (priorKey !== undefined) {
+ execIteration(priorKey, i - 1);
+ }
+ priorKey = key;
+ i++;
+ });
+ if (priorKey !== undefined) {
+ execIteration(priorKey, i - 1, true);
+ }
+ })();
+ }
+ }
+
+ if (i === 0) {
+ ret = inverse(this);
+ }
+
+ return ret;
+ });
+ };
+
+ module.exports = exports['default'];
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; })()));
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ module.exports = { "default": __webpack_require__(13), __esModule: true };
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ __webpack_require__(14);
+ module.exports = __webpack_require__(20).Object.keys;
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ // 19.1.2.14 Object.keys(O)
+ let toObject = __webpack_require__(15);
+
+ __webpack_require__(17)('keys', function($keys){
+ return function keys(it){
+ return $keys(toObject(it));
+ };
+ });
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ // 7.1.13 ToObject(argument)
+ let defined = __webpack_require__(16);
+ module.exports = function(it){
+ return Object(defined(it));
+ };
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports) {
+
+ // 7.2.1 RequireObjectCoercible(argument)
+ module.exports = function(it){
+ if(it == undefined){throw TypeError("Can't call method on " + it);}
+ return it;
+ };
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ // most Object methods by ES6 should accept primitives
+ let $export = __webpack_require__(18)
+ , core = __webpack_require__(20)
+ , fails = __webpack_require__(23);
+ module.exports = function(KEY, exec){
+ let fn = (core.Object || {})[KEY] || Object[KEY]
+ , exp = {};
+ exp[KEY] = exec(fn);
+ $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp);
+ };
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ let global = __webpack_require__(19)
+ , core = __webpack_require__(20)
+ , ctx = __webpack_require__(21)
+ , PROTOTYPE = 'prototype';
+
+ var $export = function(type, name, source){
+ let IS_FORCED = type & $export.F
+ , IS_GLOBAL = type & $export.G
+ , IS_STATIC = type & $export.S
+ , IS_PROTO = type & $export.P
+ , IS_BIND = type & $export.B
+ , IS_WRAP = type & $export.W
+ , exports = IS_GLOBAL ? core : core[name] || (core[name] = {})
+ , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]
+ , key, own, out;
+ if(IS_GLOBAL){source = name;}
+ for(key in source){
+ // contains in native
+ own = !IS_FORCED && target && key in target;
+ if(own && key in exports){continue;}
+ // export native or passed
+ out = own ? target[key] : source[key];
+ // prevent global pollution for namespaces
+ exports[key] = IS_GLOBAL && typeof target[key] !== 'function' ? source[key]
+ // bind timers to global for call from export context
+ : IS_BIND && own ? ctx(out, global)
+ // wrap global constructors for prevent change them in library
+ : IS_WRAP && target[key] == out ? (function(C){
+ let F = function(param){
+ return this instanceof C ? new C(param) : C(param);
+ };
+ F[PROTOTYPE] = C[PROTOTYPE];
+ return F;
+ // make static versions for prototype methods
+ })(out) : IS_PROTO && typeof out === 'function' ? ctx(Function.call, out) : out;
+ if(IS_PROTO){(exports[PROTOTYPE] || (exports[PROTOTYPE] = {}))[key] = out;}
+ }
+ };
+ // type bitmap
+ $export.F = 1; // forced
+ $export.G = 2; // global
+ $export.S = 4; // static
+ $export.P = 8; // proto
+ $export.B = 16; // bind
+ $export.W = 32; // wrap
+ module.exports = $export;
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports) {
+
+ // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
+ let global = module.exports = typeof window !== 'undefined' && window.Math == Math
+ ? window : typeof self !== 'undefined' && self.Math == Math ? self : Function('return this')();
+ if(typeof __g === 'number'){__g = global;} // eslint-disable-line no-undef
+
+/***/ }),
+/* 20 */
+/***/ (function(module, exports) {
+
+ let core = module.exports = {version: '1.2.6'};
+ if(typeof __e === 'number'){__e = core;} // eslint-disable-line no-undef
+
+/***/ }),
+/* 21 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ // optional / simple context binding
+ let aFunction = __webpack_require__(22);
+ module.exports = function(fn, that, length){
+ aFunction(fn);
+ if(that === undefined){return fn;}
+ switch(length){
+ case 1: return function(a){
+ return fn.call(that, a);
+ };
+ case 2: return function(a, b){
+ return fn.call(that, a, b);
+ };
+ case 3: return function(a, b, c){
+ return fn.call(that, a, b, c);
+ };
+ }
+ return function(/* ...args */){
+ return fn.apply(that, arguments);
+ };
+ };
+
+/***/ }),
+/* 22 */
+/***/ (function(module, exports) {
+
+ module.exports = function(it){
+ if(typeof it !== 'function'){throw TypeError(it + ' is not a function!');}
+ return it;
+ };
+
+/***/ }),
+/* 23 */
+/***/ (function(module, exports) {
+
+ module.exports = function(exec){
+ try {
+ return !!exec();
+ } catch(e){
+ return true;
+ }
+ };
+
+/***/ }),
+/* 24 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+
+ let _exception = __webpack_require__(5);
+
+ let _exception2 = _interopRequireDefault(_exception);
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('helperMissing', function () /* [args, ]options */{
+ if (arguments.length === 1) {
+ // A missing field in a {{foo}} construct.
+ return undefined;
+ } else {
+ // Someone is actually trying to call something, blow up.
+ throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"');
+ }
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 25 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+
+ let _utils = __webpack_require__(4);
+
+ let _exception = __webpack_require__(5);
+
+ let _exception2 = _interopRequireDefault(_exception);
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('if', function (conditional, options) {
+ if (arguments.length != 2) {
+ throw new _exception2['default']('#if requires exactly one argument');
+ }
+ if (_utils.isFunction(conditional)) {
+ conditional = conditional.call(this);
+ }
+
+ // Default behavior is to render the positive path if the value is truthy and not empty.
+ // The `includeZero` option may be set to treat the condtional as purely not empty based on the
+ // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
+ if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) {
+ return options.inverse(this);
+ } else {
+ return options.fn(this);
+ }
+ });
+
+ instance.registerHelper('unless', function (conditional, options) {
+ if (arguments.length != 2) {
+ throw new _exception2['default']('#unless requires exactly one argument');
+ }
+ return instance.helpers['if'].call(this, conditional, {
+ fn: options.inverse,
+ inverse: options.fn,
+ hash: options.hash
+ });
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 26 */
+/***/ (function(module, exports) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('log', function () /* message, options */{
+ let args = [undefined],
+ options = arguments[arguments.length - 1];
+ for (let i = 0; i < arguments.length - 1; i++) {
+ args.push(arguments[i]);
+ }
+
+ let level = 1;
+ if (options.hash.level != null) {
+ level = options.hash.level;
+ } else if (options.data && options.data.level != null) {
+ level = options.data.level;
+ }
+ args[0] = level;
+
+ instance.log.apply(instance, args);
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 27 */
+/***/ (function(module, exports) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('lookup', function (obj, field, options) {
+ if (!obj) {
+ // Note for 5.0: Change to "obj == null" in 5.0
+ return obj;
+ }
+ return options.lookupProperty(obj, field);
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 28 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+
+ let _utils = __webpack_require__(4);
+
+ let _exception = __webpack_require__(5);
+
+ let _exception2 = _interopRequireDefault(_exception);
+
+ exports['default'] = function (instance) {
+ instance.registerHelper('with', function (context, options) {
+ if (arguments.length != 2) {
+ throw new _exception2['default']('#with requires exactly one argument');
+ }
+ if (_utils.isFunction(context)) {
+ context = context.call(this);
+ }
+
+ let fn = options.fn;
+
+ if (!_utils.isEmpty(context)) {
+ let data = options.data;
+ if (options.data && options.ids) {
+ data = _utils.createFrame(options.data);
+ data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]);
+ }
+
+ return fn(context, {
+ data,
+ blockParams: _utils.blockParams([context], [data && data.contextPath])
+ });
+ } else {
+ return options.inverse(this);
+ }
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 29 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+ exports.registerDefaultDecorators = registerDefaultDecorators;
+
+ let _decoratorsInline = __webpack_require__(30);
+
+ let _decoratorsInline2 = _interopRequireDefault(_decoratorsInline);
+
+ function registerDefaultDecorators(instance) {
+ _decoratorsInline2['default'](instance);
+ }
+
+/***/ }),
+/* 30 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ let _utils = __webpack_require__(4);
+
+ exports['default'] = function (instance) {
+ instance.registerDecorator('inline', function (fn, props, container, options) {
+ let ret = fn;
+ if (!props.partials) {
+ props.partials = {};
+ ret = function (context, options) {
+ // Create a new partials stack frame prior to exec.
+ let original = container.partials;
+ container.partials = _utils.extend({}, original, props.partials);
+ let ret = fn(context, options);
+ container.partials = original;
+ return ret;
+ };
+ }
+
+ props.partials[options.args[0]] = options.fn;
+
+ return ret;
+ });
+ };
+
+ module.exports = exports['default'];
+
+/***/ }),
+/* 31 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ let _utils = __webpack_require__(4);
+
+ var logger = {
+ methodMap: ['debug', 'info', 'warn', 'error'],
+ level: 'info',
+
+ // Maps a given level value to the `methodMap` indexes above.
+ lookupLevel: function lookupLevel(level) {
+ if (typeof level === 'string') {
+ let levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase());
+ if (levelMap >= 0) {
+ level = levelMap;
+ } else {
+ level = parseInt(level, 10);
+ }
+ }
+
+ return level;
+ },
+
+ // Can be overridden in the host environment
+ log: function log(level) {
+ level = logger.lookupLevel(level);
+
+ if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) {
+ let method = logger.methodMap[level];
+ // eslint-disable-next-line no-console
+ if (!console[method]) {
+ method = 'log';
+ }
+
+ for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ message[_key - 1] = arguments[_key];
+ }
+
+ console[method].apply(console, message); // eslint-disable-line no-console
+ }
+ }
+ };
+
+ exports['default'] = logger;
+ module.exports = exports['default'];
+
+/***/ }),
+/* 32 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _Object$create = __webpack_require__(33)['default'];
+
+ let _Object$keys = __webpack_require__(12)['default'];
+
+ let _interopRequireWildcard = __webpack_require__(1)['default'];
+
+ exports.__esModule = true;
+ exports.createProtoAccessControl = createProtoAccessControl;
+ exports.resultIsAllowed = resultIsAllowed;
+ exports.resetLoggedProperties = resetLoggedProperties;
+
+ let _createNewLookupObject = __webpack_require__(35);
+
+ let _logger = __webpack_require__(31);
+
+ let logger = _interopRequireWildcard(_logger);
+
+ let loggedProperties = _Object$create(null);
+
+ function createProtoAccessControl(runtimeOptions) {
+ let defaultMethodWhiteList = _Object$create(null);
+ defaultMethodWhiteList['constructor'] = false;
+ defaultMethodWhiteList['__defineGetter__'] = false;
+ defaultMethodWhiteList['__defineSetter__'] = false;
+ defaultMethodWhiteList['__lookupGetter__'] = false;
+
+ let defaultPropertyWhiteList = _Object$create(null);
+ // eslint-disable-next-line no-proto
+ defaultPropertyWhiteList['__proto__'] = false;
+
+ return {
+ properties: {
+ whitelist: _createNewLookupObject.createNewLookupObject(defaultPropertyWhiteList, runtimeOptions.allowedProtoProperties),
+ defaultValue: runtimeOptions.allowProtoPropertiesByDefault
+ },
+ methods: {
+ whitelist: _createNewLookupObject.createNewLookupObject(defaultMethodWhiteList, runtimeOptions.allowedProtoMethods),
+ defaultValue: runtimeOptions.allowProtoMethodsByDefault
+ }
+ };
+ }
+
+ function resultIsAllowed(result, protoAccessControl, propertyName) {
+ if (typeof result === 'function') {
+ return checkWhiteList(protoAccessControl.methods, propertyName);
+ } else {
+ return checkWhiteList(protoAccessControl.properties, propertyName);
+ }
+ }
+
+ function checkWhiteList(protoAccessControlForType, propertyName) {
+ if (protoAccessControlForType.whitelist[propertyName] !== undefined) {
+ return protoAccessControlForType.whitelist[propertyName] === true;
+ }
+ if (protoAccessControlForType.defaultValue !== undefined) {
+ return protoAccessControlForType.defaultValue;
+ }
+ logUnexpecedPropertyAccessOnce(propertyName);
+ return false;
+ }
+
+ function logUnexpecedPropertyAccessOnce(propertyName) {
+ if (loggedProperties[propertyName] !== true) {
+ loggedProperties[propertyName] = true;
+ logger.log('error', 'Handlebars: Access has been denied to resolve the property "' + propertyName + '" because it is not an "own property" of its parent.\n' + 'You can add a runtime option to disable the check or this warning:\n' + 'See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details');
+ }
+ }
+
+ function resetLoggedProperties() {
+ _Object$keys(loggedProperties).forEach(function (propertyName) {
+ delete loggedProperties[propertyName];
+ });
+ }
+
+/***/ }),
+/* 33 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ module.exports = { "default": __webpack_require__(34), __esModule: true };
+
+/***/ }),
+/* 34 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ let $ = __webpack_require__(8);
+ module.exports = function create(P, D){
+ return $.create(P, D);
+ };
+
+/***/ }),
+/* 35 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _Object$create = __webpack_require__(33)['default'];
+
+ exports.__esModule = true;
+ exports.createNewLookupObject = createNewLookupObject;
+
+ let _utils = __webpack_require__(4);
+
+ /**
+ * Create a new object with "null"-prototype to avoid truthy results on prototype properties.
+ * The resulting object can be used with "object[property]" to check if a property exists
+ * @param {...object} sources a varargs parameter of source objects that will be merged
+ * @returns {object}
+ */
+
+ function createNewLookupObject() {
+ for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
+ sources[_key] = arguments[_key];
+ }
+
+ return _utils.extend.apply(undefined, [_Object$create(null)].concat(sources));
+ }
+
+/***/ }),
+/* 36 */
+/***/ (function(module, exports) {
+
+ // Build out our basic SafeString type
+ 'use strict';
+
+ exports.__esModule = true;
+ function SafeString(string) {
+ this.string = string;
+ }
+
+ SafeString.prototype.toString = SafeString.prototype.toHTML = function () {
+ return '' + this.string;
+ };
+
+ exports['default'] = SafeString;
+ module.exports = exports['default'];
+
+/***/ }),
+/* 37 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ let _Object$seal = __webpack_require__(38)['default'];
+
+ let _Object$keys = __webpack_require__(12)['default'];
+
+ let _interopRequireWildcard = __webpack_require__(1)['default'];
+
+ let _interopRequireDefault = __webpack_require__(2)['default'];
+
+ exports.__esModule = true;
+ exports.checkRevision = checkRevision;
+ exports.template = template;
+ exports.wrapProgram = wrapProgram;
+ exports.resolvePartial = resolvePartial;
+ exports.invokePartial = invokePartial;
+ exports.noop = noop;
+
+ let _utils = __webpack_require__(4);
+
+ let Utils = _interopRequireWildcard(_utils);
+
+ let _exception = __webpack_require__(5);
+
+ let _exception2 = _interopRequireDefault(_exception);
+
+ let _base = __webpack_require__(3);
+
+ let _helpers = __webpack_require__(9);
+
+ let _internalWrapHelper = __webpack_require__(42);
+
+ let _internalProtoAccess = __webpack_require__(32);
+
+ function checkRevision(compilerInfo) {
+ let compilerRevision = compilerInfo && compilerInfo[0] || 1,
+ currentRevision = _base.COMPILER_REVISION;
+
+ if (compilerRevision >= _base.LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= _base.COMPILER_REVISION) {
+ return;
+ }
+
+ if (compilerRevision < _base.LAST_COMPATIBLE_COMPILER_REVISION) {
+ let runtimeVersions = _base.REVISION_CHANGES[currentRevision],
+ compilerVersions = _base.REVISION_CHANGES[compilerRevision];
+ throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
+ } else {
+ // Use the embedded version info since the runtime doesn't know about this revision yet
+ throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
+ }
+ }
+
+ function template(templateSpec, env) {
+ /* istanbul ignore next */
+ if (!env) {
+ throw new _exception2['default']('No environment passed to template');
+ }
+ if (!templateSpec || !templateSpec.main) {
+ throw new _exception2['default']('Unknown template object: ' + typeof templateSpec);
+ }
+
+ templateSpec.main.decorator = templateSpec.main_d;
+
+ // Note: Using env.VM references rather than local var references throughout this section to allow
+ // for external users to override these as pseudo-supported APIs.
+ env.VM.checkRevision(templateSpec.compiler);
+
+ // backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0)
+ let templateWasPrecompiledWithCompilerV7 = templateSpec.compiler && templateSpec.compiler[0] === 7;
+
+ function invokePartialWrapper(partial, context, options) {
+ if (options.hash) {
+ context = Utils.extend({}, context, options.hash);
+ if (options.ids) {
+ options.ids[0] = true;
+ }
+ }
+ partial = env.VM.resolvePartial.call(this, partial, context, options);
+
+ let extendedOptions = Utils.extend({}, options, {
+ hooks: this.hooks,
+ protoAccessControl: this.protoAccessControl
+ });
+
+ let result = env.VM.invokePartial.call(this, partial, context, extendedOptions);
+
+ if (result == null && env.compile) {
+ options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
+ result = options.partials[options.name](context, extendedOptions);
+ }
+ if (result != null) {
+ if (options.indent) {
+ let lines = result.split('\n');
+ for (let i = 0, l = lines.length; i < l; i++) {
+ if (!lines[i] && i + 1 === l) {
+ break;
+ }
+
+ lines[i] = options.indent + lines[i];
+ }
+ result = lines.join('\n');
+ }
+ return result;
+ } else {
+ throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode');
+ }
+ }
+
+ // Just add water
+ var container = {
+ strict: function strict(obj, name, loc) {
+ if (!obj || !(name in obj)) {
+ throw new _exception2['default']('"' + name + '" not defined in ' + obj, {
+ loc
+ });
+ }
+ return container.lookupProperty(obj, name);
+ },
+ lookupProperty: function lookupProperty(parent, propertyName) {
+ let result = parent[propertyName];
+ if (result == null) {
+ return result;
+ }
+ if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
+ return result;
+ }
+
+ if (_internalProtoAccess.resultIsAllowed(result, container.protoAccessControl, propertyName)) {
+ return result;
+ }
+ return undefined;
+ },
+ lookup: function lookup(depths, name) {
+ let len = depths.length;
+ for (let i = 0; i < len; i++) {
+ let result = depths[i] && container.lookupProperty(depths[i], name);
+ if (result != null) {
+ return depths[i][name];
+ }
+ }
+ },
+ lambda: function lambda(current, context) {
+ return typeof current === 'function' ? current.call(context) : current;
+ },
+
+ escapeExpression: Utils.escapeExpression,
+ invokePartial: invokePartialWrapper,
+
+ fn: function fn(i) {
+ let ret = templateSpec[i];
+ ret.decorator = templateSpec[i + '_d'];
+ return ret;
+ },
+
+ programs: [],
+ program: function program(i, data, declaredBlockParams, blockParams, depths) {
+ let programWrapper = this.programs[i],
+ fn = this.fn(i);
+ if (data || depths || blockParams || declaredBlockParams) {
+ programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths);
+ } else if (!programWrapper) {
+ programWrapper = this.programs[i] = wrapProgram(this, i, fn);
+ }
+ return programWrapper;
+ },
+
+ data: function data(value, depth) {
+ while (value && depth--) {
+ value = value._parent;
+ }
+ return value;
+ },
+ mergeIfNeeded: function mergeIfNeeded(param, common) {
+ let obj = param || common;
+
+ if (param && common && param !== common) {
+ obj = Utils.extend({}, common, param);
+ }
+
+ return obj;
+ },
+ // An empty object to use as replacement for null-contexts
+ nullContext: _Object$seal({}),
+
+ noop: env.VM.noop,
+ compilerInfo: templateSpec.compiler
+ };
+
+ function ret(context) {
+ let options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ let data = options.data;
+
+ ret._setup(options);
+ if (!options.partial && templateSpec.useData) {
+ data = initData(context, data);
+ }
+ let depths = undefined,
+ blockParams = templateSpec.useBlockParams ? [] : undefined;
+ if (templateSpec.useDepths) {
+ if (options.depths) {
+ depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths;
+ } else {
+ depths = [context];
+ }
+ }
+
+ function main(context /*, options*/) {
+ return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths);
+ }
+
+ main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams);
+ return main(context, options);
+ }
+
+ ret.isTop = true;
+
+ ret._setup = function (options) {
+ if (!options.partial) {
+ let mergedHelpers = Utils.extend({}, env.helpers, options.helpers);
+ wrapHelpersToPassLookupProperty(mergedHelpers, container);
+ container.helpers = mergedHelpers;
+
+ if (templateSpec.usePartial) {
+ // Use mergeIfNeeded here to prevent compiling global partials multiple times
+ container.partials = container.mergeIfNeeded(options.partials, env.partials);
+ }
+ if (templateSpec.usePartial || templateSpec.useDecorators) {
+ container.decorators = Utils.extend({}, env.decorators, options.decorators);
+ }
+
+ container.hooks = {};
+ container.protoAccessControl = _internalProtoAccess.createProtoAccessControl(options);
+
+ let keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7;
+ _helpers.moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
+ _helpers.moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
+ } else {
+ container.protoAccessControl = options.protoAccessControl; // internal option
+ container.helpers = options.helpers;
+ container.partials = options.partials;
+ container.decorators = options.decorators;
+ container.hooks = options.hooks;
+ }
+ };
+
+ ret._child = function (i, data, blockParams, depths) {
+ if (templateSpec.useBlockParams && !blockParams) {
+ throw new _exception2['default']('must pass block params');
+ }
+ if (templateSpec.useDepths && !depths) {
+ throw new _exception2['default']('must pass parent depths');
+ }
+
+ return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths);
+ };
+ return ret;
+ }
+
+ function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) {
+ function prog(context) {
+ let options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ let currentDepths = depths;
+ if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) {
+ currentDepths = [context].concat(depths);
+ }
+
+ return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths);
+ }
+
+ prog = executeDecorators(fn, prog, container, depths, data, blockParams);
+
+ prog.program = i;
+ prog.depth = depths ? depths.length : 0;
+ prog.blockParams = declaredBlockParams || 0;
+ return prog;
+ }
+
+ /**
+ * This is currently part of the official API, therefore implementation details should not be changed.
+ */
+
+ function resolvePartial(partial, context, options) {
+ if (!partial) {
+ if (options.name === '@partial-block') {
+ partial = options.data['partial-block'];
+ } else {
+ partial = options.partials[options.name];
+ }
+ } else if (!partial.call && !options.name) {
+ // This is a dynamic partial that returned a string
+ options.name = partial;
+ partial = options.partials[partial];
+ }
+ return partial;
+ }
+
+ function invokePartial(partial, context, options) {
+ // Use the current closure context to save the partial-block if this partial
+ let currentPartialBlock = options.data && options.data['partial-block'];
+ options.partial = true;
+ if (options.ids) {
+ options.data.contextPath = options.ids[0] || options.data.contextPath;
+ }
+
+ let partialBlock = undefined;
+ if (options.fn && options.fn !== noop) {
+ (function () {
+ options.data = _base.createFrame(options.data);
+ // Wrapper function to get access to currentPartialBlock from the closure
+ let fn = options.fn;
+ partialBlock = options.data['partial-block'] = function partialBlockWrapper(context) {
+ let options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ // Restore the partial-block from the closure for the execution of the block
+ // i.e. the part inside the block of the partial call.
+ options.data = _base.createFrame(options.data);
+ options.data['partial-block'] = currentPartialBlock;
+ return fn(context, options);
+ };
+ if (fn.partials) {
+ options.partials = Utils.extend({}, options.partials, fn.partials);
+ }
+ })();
+ }
+
+ if (partial === undefined && partialBlock) {
+ partial = partialBlock;
+ }
+
+ if (partial === undefined) {
+ throw new _exception2['default']('The partial ' + options.name + ' could not be found');
+ } else if (partial instanceof Function) {
+ return partial(context, options);
+ }
+ }
+
+ function noop() {
+ return '';
+ }
+
+ function initData(context, data) {
+ if (!data || !('root' in data)) {
+ data = data ? _base.createFrame(data) : {};
+ data.root = context;
+ }
+ return data;
+ }
+
+ function executeDecorators(fn, prog, container, depths, data, blockParams) {
+ if (fn.decorator) {
+ let props = {};
+ prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths);
+ Utils.extend(prog, props);
+ }
+ return prog;
+ }
+
+ function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
+ _Object$keys(mergedHelpers).forEach(function (helperName) {
+ let helper = mergedHelpers[helperName];
+ mergedHelpers[helperName] = passLookupPropertyOption(helper, container);
+ });
+ }
+
+ function passLookupPropertyOption(helper, container) {
+ let lookupProperty = container.lookupProperty;
+ return _internalWrapHelper.wrapHelper(helper, function (options) {
+ return Utils.extend({ lookupProperty }, options);
+ });
+ }
+
+/***/ }),
+/* 38 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ module.exports = { "default": __webpack_require__(39), __esModule: true };
+
+/***/ }),
+/* 39 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ __webpack_require__(40);
+ module.exports = __webpack_require__(20).Object.seal;
+
+/***/ }),
+/* 40 */
+/***/ (function(module, exports, __webpack_require__) {
+
+ // 19.1.2.17 Object.seal(O)
+ let isObject = __webpack_require__(41);
+
+ __webpack_require__(17)('seal', function($seal){
+ return function seal(it){
+ return $seal && isObject(it) ? $seal(it) : it;
+ };
+ });
+
+/***/ }),
+/* 41 */
+/***/ (function(module, exports) {
+
+ module.exports = function(it){
+ return typeof it === 'object' ? it !== null : typeof it === 'function';
+ };
+
+/***/ }),
+/* 42 */
+/***/ (function(module, exports) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ exports.wrapHelper = wrapHelper;
+
+ function wrapHelper(helper, transformOptionsFn) {
+ if (typeof helper !== 'function') {
+ // This should not happen, but apparently it does in https://github.com/wycats/handlebars.js/issues/1639
+ // We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function.
+ return helper;
+ }
+ let wrapper = function wrapper() /* dynamic arguments */{
+ let options = arguments[arguments.length - 1];
+ arguments[arguments.length - 1] = transformOptionsFn(options);
+ return helper.apply(this, arguments);
+ };
+ return wrapper;
+ }
+
+/***/ }),
+/* 43 */
+/***/ (function(module, exports) {
+
+ /* WEBPACK VAR INJECTION */(function(global) {'use strict';
+
+ exports.__esModule = true;
+
+ exports['default'] = function (Handlebars) {
+ /* istanbul ignore next */
+ let root = typeof global !== 'undefined' ? global : window,
+ $Handlebars = root.Handlebars;
+ /* istanbul ignore next */
+ Handlebars.noConflict = function () {
+ if (root.Handlebars === Handlebars) {
+ root.Handlebars = $Handlebars;
+ }
+ return Handlebars;
+ };
+ };
+
+ module.exports = exports['default'];
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; })()));
+
+/***/ })
+/******/ ]);
+});
+;
diff --git a/assets/javascripts/legacy/itsatrap.js b/assets/javascripts/legacy/itsatrap.js
new file mode 100644
index 00000000..ae5ad391
--- /dev/null
+++ b/assets/javascripts/legacy/itsatrap.js
@@ -0,0 +1,1161 @@
+// discourse-skip-module
+
+/*global define:false */
+/**
+ * Copyright 2012-2017 Craig Campbell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ItsATrap is a simple keyboard shortcut library for Javascript with
+ * no external dependencies
+ *
+ * @version 2.0.1
+ * @url github.com/discourse/itsatrap
+ */
+(function(window, document, undefined) {
+ // Check if itsatrap is used inside browser, if not, return
+ if (!window) {
+ return;
+ }
+
+ /**
+ * mapping of special keycodes to their corresponding keys
+ *
+ * everything in this dictionary cannot use keypress events
+ * so it has to be here to map to the correct keycodes for
+ * keyup/keydown events
+ *
+ * @type {Object}
+ */
+ let _MAP = {
+ 8: "backspace",
+ 9: "tab",
+ 13: "enter",
+ 16: "shift",
+ 17: "ctrl",
+ 18: "alt",
+ 20: "capslock",
+ 27: "esc",
+ 32: "space",
+ 33: "pageup",
+ 34: "pagedown",
+ 35: "end",
+ 36: "home",
+ 37: "left",
+ 38: "up",
+ 39: "right",
+ 40: "down",
+ 45: "ins",
+ 46: "del",
+ 91: "meta",
+ 93: "meta",
+ 224: "meta"
+ };
+
+ /**
+ * mapping for special characters so they can support
+ *
+ * this dictionary is only used incase you want to bind a
+ * keyup or keydown event to one of these keys
+ *
+ * @type {Object}
+ */
+ let _KEYCODE_MAP = {
+ 106: "*",
+ 107: "+",
+ 109: "-",
+ 110: ".",
+ 111: "/",
+ 186: ";",
+ 187: "=",
+ 188: ",",
+ 189: "-",
+ 190: ".",
+ 191: "/",
+ 192: "`",
+ 219: "[",
+ 220: "\\",
+ 221: "]",
+ 222: "'"
+ };
+
+ /**
+ * this is a mapping of keys that require shift on a US keypad
+ * back to the non shift equivelents
+ *
+ * this is so you can use keyup events with these keys
+ *
+ * note that this will only work reliably on US keyboards
+ *
+ * @type {Object}
+ */
+ let _SHIFT_MAP = {
+ "~": "`",
+ "!": "1",
+ "@": "2",
+ "#": "3",
+ $: "4",
+ "%": "5",
+ "^": "6",
+ "&": "7",
+ "*": "8",
+ "(": "9",
+ ")": "0",
+ _: "-",
+ "+": "=",
+ ":": ";",
+ '"': "'",
+ "<": ",",
+ ">": ".",
+ "?": "/",
+ "|": "\\"
+ };
+
+ /**
+ * this is a list of special strings you can use to map
+ * to modifier keys when you specify your keyboard shortcuts
+ *
+ * @type {Object}
+ */
+ let _SPECIAL_ALIASES = {
+ option: "alt",
+ command: "meta",
+ return: "enter",
+ escape: "esc",
+ plus: "+",
+ mod: /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? "meta" : "ctrl"
+ };
+
+ /**
+ * variable to store the flipped version of _MAP from above
+ * needed to check if we should use keypress or not when no action
+ * is specified
+ *
+ * @type {Object|undefined}
+ */
+ let _REVERSE_MAP;
+
+ /**
+ * holds a reference to global bindings
+ *
+ * @type {Object|undefined}
+ */
+ let _globalCallbacks = {};
+
+ /**
+ * loop through the f keys, f1 to f19 and add them to the map
+ * programatically
+ */
+ for (var i = 1; i < 20; ++i) {
+ _MAP[111 + i] = "f" + i;
+ }
+
+ /**
+ * loop through to map numbers on the numeric keypad
+ */
+ for (i = 0; i <= 9; ++i) {
+ // This needs to use a string cause otherwise since 0 is falsey
+ // itsatrap will never fire for numpad 0 pressed as part of a keydown
+ // event.
+ //
+ // @see https://github.com/ccampbell/itsatrap/pull/258
+ _MAP[i + 96] = i.toString();
+ }
+
+ /**
+ * cross browser add event method
+ *
+ * @param {Element|HTMLDocument} object
+ * @param {string} type
+ * @param {EventListenerOrEventListenerObject} callback
+ * @returns void
+ */
+ function _addEvent(object, type, callback) {
+ if (object.addEventListener) {
+ object.addEventListener(type, callback, false);
+ return;
+ }
+
+ object.attachEvent("on" + type, callback);
+ }
+
+ /**
+ * cross browser remove event method
+ *
+ * @param {Element|HTMLDocument} object
+ * @param {string} type
+ * @param {EventListenerOrEventListenerObject} callback
+ * @returns void
+ */
+ function _removeEvent(object, type, callback) {
+ if (object.removeEventListener) {
+ object.removeEventListener(type, callback, false);
+ return;
+ }
+ object.detachEvent("on" + type, callback);
+ }
+
+ /**
+ * takes the event and returns the key character
+ *
+ * @param {Event} e
+ * @return {string}
+ */
+ function _characterFromEvent(e) {
+ // for keypress events we should return the character as is
+ if (e.type == "keypress") {
+ let character = String.fromCharCode(e.which);
+
+ // if the shift key is not pressed then it is safe to assume
+ // that we want the character to be lowercase. this means if
+ // you accidentally have caps lock on then your key bindings
+ // will continue to work
+ //
+ // the only side effect that might not be desired is if you
+ // bind something like 'A' cause you want to trigger an
+ // event when capital A is pressed caps lock will no longer
+ // trigger the event. shift+a will though.
+ if (!e.shiftKey) {
+ character = character.toLowerCase();
+ }
+
+ return character;
+ }
+
+ // for non keypress events the special maps are needed
+ if (_MAP[e.which]) {
+ return _MAP[e.which];
+ }
+
+ if (_KEYCODE_MAP[e.which]) {
+ return _KEYCODE_MAP[e.which];
+ }
+
+ // if it is not in the special map
+
+ // with keydown and keyup events the character seems to always
+ // come in as an uppercase character whether you are pressing shift
+ // or not. we should make sure it is always lowercase for comparisons
+ return String.fromCharCode(e.which).toLowerCase();
+ }
+
+ /**
+ * checks if two arrays are equal
+ *
+ * @param {Array} modifiers1
+ * @param {Array} modifiers2
+ * @returns {boolean}
+ */
+ function _modifiersMatch(modifiers1, modifiers2) {
+ return modifiers1.sort().join(",") === modifiers2.sort().join(",");
+ }
+
+ /**
+ * takes a key event and figures out what the modifiers are
+ *
+ * @param {Event} e
+ * @returns {Array}
+ */
+ function _eventModifiers(e) {
+ let modifiers = [];
+
+ if (e.shiftKey) {
+ modifiers.push("shift");
+ }
+
+ if (e.altKey) {
+ modifiers.push("alt");
+ }
+
+ if (e.ctrlKey) {
+ modifiers.push("ctrl");
+ }
+
+ if (e.metaKey) {
+ modifiers.push("meta");
+ }
+
+ return modifiers;
+ }
+
+ /**
+ * prevents default for this event
+ *
+ * @param {Event} e
+ * @returns void
+ */
+ function _preventDefault(e) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ return;
+ }
+
+ e.returnValue = false;
+ }
+
+ /**
+ * stops propogation for this event
+ *
+ * @param {Event} e
+ * @returns void
+ */
+ function _stopPropagation(e) {
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ return;
+ }
+
+ e.cancelBubble = true;
+ }
+
+ /**
+ * determines if the keycode specified is a modifier key or not
+ *
+ * @param {string} key
+ * @returns {boolean}
+ */
+ function _isModifier(key) {
+ return key == "shift" || key == "ctrl" || key == "alt" || key == "meta";
+ }
+
+ /**
+ * reverses the map lookup so that we can look for specific keys
+ * to see what can and can't use keypress
+ *
+ * @return {Object}
+ */
+ function _getReverseMap() {
+ if (!_REVERSE_MAP) {
+ _REVERSE_MAP = {};
+ for (let key in _MAP) {
+ // pull out the numeric keypad from here cause keypress should
+ // be able to detect the keys from the character
+ if (key > 95 && key < 112) {
+ continue;
+ }
+
+ if (_MAP.hasOwnProperty(key)) {
+ _REVERSE_MAP[_MAP[key]] = key;
+ }
+ }
+ }
+ return _REVERSE_MAP;
+ }
+
+ /**
+ * picks the best action based on the key combination
+ *
+ * @param {string} key - character for key
+ * @param {Array} modifiers
+ * @param {string=} action passed in
+ */
+ function _pickBestAction(key, modifiers, action) {
+ // if no action was picked in we should try to pick the one
+ // that we think would work best for this key
+ if (!action) {
+ action = _getReverseMap()[key] ? "keydown" : "keypress";
+ }
+
+ // modifier keys don't work as expected with keypress,
+ // switch to keydown
+ if (action == "keypress" && modifiers.length) {
+ action = "keydown";
+ }
+
+ return action;
+ }
+
+ /**
+ * Converts from a string key combination to an array
+ *
+ * @param {string} combination like "command+shift+l"
+ * @return {Array}
+ */
+ function _keysFromString(combination) {
+ if (combination === "+") {
+ return ["+"];
+ }
+
+ combination = combination.replace(/\+{2}/g, "+plus");
+ return combination.split("+");
+ }
+
+ /**
+ * Gets info for a specific key combination
+ *
+ * @param {string} combination key combination ("command+s" or "a" or "*")
+ * @param {string=} action
+ * @returns {Object}
+ */
+ function _getKeyInfo(combination, action) {
+ let keys;
+ let key;
+ let i;
+ let modifiers = [];
+
+ // take the keys from this pattern and figure out what the actual
+ // pattern is all about
+ keys = _keysFromString(combination);
+
+ for (i = 0; i < keys.length; ++i) {
+ key = keys[i];
+
+ // normalize key names
+ if (_SPECIAL_ALIASES[key]) {
+ key = _SPECIAL_ALIASES[key];
+ }
+
+ // if this is not a keypress event then we should
+ // be smart about using shift keys
+ // this will only work for US keyboards however
+ if (action && action != "keypress" && _SHIFT_MAP[key]) {
+ key = _SHIFT_MAP[key];
+ modifiers.push("shift");
+ }
+
+ // if this key is a modifier then add it to the list of modifiers
+ if (_isModifier(key)) {
+ modifiers.push(key);
+ }
+ }
+
+ // depending on what the key combination is
+ // we will try to pick the best event for it
+ action = _pickBestAction(key, modifiers, action);
+
+ return {
+ key,
+ modifiers,
+ action
+ };
+ }
+
+ function _belongsTo(element, ancestor) {
+ if (element === null || element === document) {
+ return false;
+ }
+
+ if (element === ancestor) {
+ return true;
+ }
+
+ return _belongsTo(element.parentNode, ancestor);
+ }
+
+ function ItsATrap(targetElement) {
+ let self = this;
+
+ targetElement = targetElement || document;
+
+ if (!(self instanceof ItsATrap)) {
+ return new ItsATrap(targetElement);
+ }
+
+ /**
+ * element to attach key events to
+ *
+ * @type {Element}
+ */
+ self.target = targetElement;
+
+ /**
+ * a list of all the callbacks setup via ItsATrap.bind()
+ *
+ * @type {Object}
+ */
+ self._callbacks = {};
+
+ /**
+ * direct map of string combinations to callbacks used for trigger()
+ *
+ * @type {Object}
+ */
+ self._directMap = {};
+
+ /**
+ * keeps track of what level each sequence is at since multiple
+ * sequences can start out with the same sequence
+ *
+ * @type {Object}
+ */
+ let _sequenceLevels = {};
+
+ /**
+ * variable to store the setTimeout call
+ *
+ * @type {null|number}
+ */
+ let _resetTimer;
+
+ /**
+ * temporary state where we will ignore the next keyup
+ *
+ * @type {boolean|string}
+ */
+ let _ignoreNextKeyup = false;
+
+ /**
+ * temporary state where we will ignore the next keypress
+ *
+ * @type {boolean}
+ */
+ let _ignoreNextKeypress = false;
+
+ /**
+ * are we currently inside of a sequence?
+ * type of action ("keyup" or "keydown" or "keypress") or false
+ *
+ * @type {boolean|string}
+ */
+ let _nextExpectedAction = false;
+
+ /**
+ * resets all sequence counters except for the ones passed in
+ *
+ * @param {Object} doNotReset
+ * @returns void
+ */
+ function _resetSequences(doNotReset) {
+ doNotReset = doNotReset || {};
+
+ let activeSequences = false,
+ key;
+
+ for (key in _sequenceLevels) {
+ if (doNotReset[key]) {
+ activeSequences = true;
+ continue;
+ }
+ _sequenceLevels[key] = 0;
+ }
+
+ if (!activeSequences) {
+ _nextExpectedAction = false;
+ }
+ }
+
+ /**
+ * finds all callbacks that match based on the keycode, modifiers,
+ * and action
+ *
+ * @param {string} character
+ * @param {Array} modifiers
+ * @param {Event|Object} e
+ * @param {string=} sequenceName - name of the sequence we are looking for
+ * @param {string=} combination
+ * @param {number=} level
+ * @returns {Array}
+ */
+ function _getMatches(
+ character,
+ modifiers,
+ e,
+ sequenceName,
+ combination,
+ level
+ ) {
+ let i;
+ let callback;
+ let matches = [];
+ let action = e.type;
+
+ // if there are no events related to this keycode
+ if (!self._callbacks[character]) {
+ return [];
+ }
+
+ // if a modifier key is coming up on its own we should allow it
+ if (action == "keyup" && _isModifier(character)) {
+ modifiers = [character];
+ }
+
+ // loop through all callbacks for the key that was pressed
+ // and see if any of them match
+ for (i = 0; i < self._callbacks[character].length; ++i) {
+ callback = self._callbacks[character][i];
+
+ // if a sequence name is not specified, but this is a sequence at
+ // the wrong level then move onto the next match
+ if (
+ !sequenceName &&
+ callback.seq &&
+ _sequenceLevels[callback.seq] != callback.level
+ ) {
+ continue;
+ }
+
+ // if the action we are looking for doesn't match the action we got
+ // then we should keep going
+ if (action != callback.action) {
+ continue;
+ }
+
+ // if this is a keypress event and the meta key and control key
+ // are not pressed that means that we need to only look at the
+ // character, otherwise check the modifiers as well
+ //
+ // chrome will not fire a keypress if meta or control is down
+ // safari will fire a keypress if meta or meta+shift is down
+ // firefox will fire a keypress if meta or control is down
+ if (
+ (action == "keypress" && !e.metaKey && !e.ctrlKey) ||
+ _modifiersMatch(modifiers, callback.modifiers)
+ ) {
+ // when you bind a combination or sequence a second time it
+ // should overwrite the first one. if a sequenceName or
+ // combination is specified in this call it does just that
+ //
+ // @todo make deleting its own method?
+ let deleteCombo = !sequenceName && callback.combo == combination;
+ let deleteSequence =
+ sequenceName &&
+ callback.seq == sequenceName &&
+ callback.level == level;
+ if (deleteCombo || deleteSequence) {
+ self._callbacks[character].splice(i, 1);
+ }
+
+ matches.push(callback);
+ }
+ }
+
+ return matches;
+ }
+
+ /**
+ * actually calls the callback function
+ *
+ * if your callback function returns false this will use the jquery
+ * convention - prevent default and stop propogation on the event
+ *
+ * @param {Function} callback
+ * @param {Event} e
+ * @returns void
+ */
+ function _fireCallback(callback, e, combo, sequence) {
+ // if this event should not happen stop here
+ if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
+ return;
+ }
+
+ if (callback(e, combo) === false) {
+ _preventDefault(e);
+ _stopPropagation(e);
+ }
+ }
+
+ /**
+ * handles a character key event
+ *
+ * @param {string} character
+ * @param {Array} modifiers
+ * @param {Event} e
+ * @returns void
+ */
+ self._handleKey = function(character, modifiers, e) {
+ let callbacks = _getMatches(character, modifiers, e);
+ let i;
+ let doNotReset = {};
+ let maxLevel = 0;
+ let processedSequenceCallback = false;
+
+ // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
+ for (i = 0; i < callbacks.length; ++i) {
+ if (callbacks[i].seq) {
+ maxLevel = Math.max(maxLevel, callbacks[i].level);
+ }
+ }
+
+ // loop through matching callbacks for this key event
+ for (i = 0; i < callbacks.length; ++i) {
+ // fire for all sequence callbacks
+ // this is because if for example you have multiple sequences
+ // bound such as "g i" and "g t" they both need to fire the
+ // callback for matching g cause otherwise you can only ever
+ // match the first one
+ if (callbacks[i].seq) {
+ // only fire callbacks for the maxLevel to prevent
+ // subsequences from also firing
+ //
+ // for example 'a option b' should not cause 'option b' to fire
+ // even though 'option b' is part of the other sequence
+ //
+ // any sequences that do not match here will be discarded
+ // below by the _resetSequences call
+ if (callbacks[i].level != maxLevel) {
+ continue;
+ }
+
+ processedSequenceCallback = true;
+
+ // keep a list of which sequences were matches for later
+ doNotReset[callbacks[i].seq] = 1;
+ _fireCallback(
+ callbacks[i].callback,
+ e,
+ callbacks[i].combo,
+ callbacks[i].seq
+ );
+ continue;
+ }
+
+ // if there were no sequence matches but we are still here
+ // that means this is a regular match so we should fire that
+ if (!processedSequenceCallback) {
+ _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
+ }
+ }
+
+ // if the key you pressed matches the type of sequence without
+ // being a modifier (ie "keyup" or "keypress") then we should
+ // reset all sequences that were not matched by this event
+ //
+ // this is so, for example, if you have the sequence "h a t" and you
+ // type "h e a r t" it does not match. in this case the "e" will
+ // cause the sequence to reset
+ //
+ // modifier keys are ignored because you can have a sequence
+ // that contains modifiers such as "enter ctrl+space" and in most
+ // cases the modifier key will be pressed before the next key
+ //
+ // also if you have a sequence such as "ctrl+b a" then pressing the
+ // "b" key will trigger a "keypress" and a "keydown"
+ //
+ // the "keydown" is expected when there is a modifier, but the
+ // "keypress" ends up matching the _nextExpectedAction since it occurs
+ // after and that causes the sequence to reset
+ //
+ // we ignore keypresses in a sequence that directly follow a keydown
+ // for the same character
+ let ignoreThisKeypress = e.type == "keypress" && _ignoreNextKeypress;
+ if (
+ e.type == _nextExpectedAction &&
+ !_isModifier(character) &&
+ !ignoreThisKeypress
+ ) {
+ _resetSequences(doNotReset);
+ }
+
+ _ignoreNextKeypress = processedSequenceCallback && e.type == "keydown";
+ };
+
+ /**
+ * handles a keydown event
+ *
+ * @param {Event} e
+ * @returns void
+ */
+ self._handleKeyEvent = function(e) {
+ // normalize e.which for key events
+ // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
+ if (typeof e.which !== "number") {
+ e.which = e.keyCode;
+ }
+
+ let character = _characterFromEvent(e);
+
+ // no character found then stop
+ if (!character) {
+ return;
+ }
+
+ // need to use === for the character check because the character can be 0
+ if (e.type == "keyup" && _ignoreNextKeyup === character) {
+ _ignoreNextKeyup = false;
+ return;
+ }
+
+ self.handleKey(character, _eventModifiers(e), e);
+ };
+
+ /**
+ * called to set a 1 second timeout on the specified sequence
+ *
+ * this is so after each key press in the sequence you have 1 second
+ * to press the next key before you have to start over
+ *
+ * @returns void
+ */
+ function _resetSequenceTimer() {
+ clearTimeout(_resetTimer);
+ _resetTimer = setTimeout(_resetSequences, 1000);
+ }
+
+ /**
+ * binds a key sequence to an event
+ *
+ * @param {string} combo - combo specified in bind call
+ * @param {Array} keys
+ * @param {Function} callback
+ * @param {string=} action
+ * @returns void
+ */
+ function _bindSequence(combo, keys, callback, action) {
+ // start off by adding a sequence level record for this combination
+ // and setting the level to 0
+ _sequenceLevels[combo] = 0;
+
+ /**
+ * callback to increase the sequence level for this sequence and reset
+ * all other sequences that were active
+ *
+ * @param {string} nextAction
+ * @returns {Function}
+ */
+ function _increaseSequence(nextAction) {
+ return function() {
+ _nextExpectedAction = nextAction;
+ ++_sequenceLevels[combo];
+ _resetSequenceTimer();
+ };
+ }
+
+ /**
+ * wraps the specified callback inside of another function in order
+ * to reset all sequence counters as soon as this sequence is done
+ *
+ * @param {Event} e
+ * @returns void
+ */
+ function _callbackAndReset(e) {
+ _fireCallback(callback, e, combo);
+
+ // we should ignore the next key up if the action is key down
+ // or keypress. this is so if you finish a sequence and
+ // release the key the final key will not trigger a keyup
+ if (action !== "keyup") {
+ _ignoreNextKeyup = _characterFromEvent(e);
+ }
+
+ // weird race condition if a sequence ends with the key
+ // another sequence begins with
+ setTimeout(_resetSequences, 10);
+ }
+
+ // loop through keys one at a time and bind the appropriate callback
+ // function. for any key leading up to the final one it should
+ // increase the sequence. after the final, it should reset all sequences
+ //
+ // if an action is specified in the original bind call then that will
+ // be used throughout. otherwise we will pass the action that the
+ // next key in the sequence should match. this allows a sequence
+ // to mix and match keypress and keydown events depending on which
+ // ones are better suited to the key provided
+ for (let i = 0; i < keys.length; ++i) {
+ let isFinal = i + 1 === keys.length;
+ let wrappedCallback = isFinal
+ ? _callbackAndReset
+ : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
+ _bindSingle(keys[i], wrappedCallback, action, combo, i);
+ }
+ }
+
+ /**
+ * binds a single keyboard combination
+ *
+ * @param {string} combination
+ * @param {Function} callback
+ * @param {string=} action
+ * @param {string=} sequenceName - name of sequence if part of sequence
+ * @param {number=} level - what part of the sequence the command is
+ * @returns void
+ */
+ function _bindSingle(combination, callback, action, sequenceName, level) {
+ // store a direct mapped reference for use with ItsATrap.trigger
+ self._directMap[combination + ":" + action] = callback;
+
+ // make sure multiple spaces in a row become a single space
+ combination = combination.replace(/\s+/g, " ");
+
+ let sequence = combination.split(" ");
+ let info;
+
+ // if this pattern is a sequence of keys then run through this method
+ // to reprocess each pattern one key at a time
+ if (sequence.length > 1) {
+ _bindSequence(combination, sequence, callback, action);
+ return;
+ }
+
+ info = _getKeyInfo(combination, action);
+
+ // make sure to initialize array if this is the first time
+ // a callback is added for this key
+ self._callbacks[info.key] = self._callbacks[info.key] || [];
+
+ // remove an existing match if there is one
+ _getMatches(
+ info.key,
+ info.modifiers,
+ { type: info.action },
+ sequenceName,
+ combination,
+ level
+ );
+
+ // add this call back to the array
+ // if it is a sequence put it at the beginning
+ // if not put it at the end
+ //
+ // this is important because the way these are processed expects
+ // the sequence ones to come first
+ self._callbacks[info.key][sequenceName ? "unshift" : "push"]({
+ callback,
+ modifiers: info.modifiers,
+ action: info.action,
+ seq: sequenceName,
+ level,
+ combo: combination
+ });
+ }
+
+ /**
+ * binds multiple combinations to the same callback
+ *
+ * @param {Array} combinations
+ * @param {Function} callback
+ * @param {string|undefined} action
+ * @returns void
+ */
+ self._bindMultiple = function(combinations, callback, action) {
+ for (let i = 0; i < combinations.length; ++i) {
+ _bindSingle(combinations[i], callback, action);
+ }
+ };
+
+ // start!
+ _addEvent(targetElement, "keypress", self._handleKeyEvent);
+ _addEvent(targetElement, "keydown", self._handleKeyEvent);
+ _addEvent(targetElement, "keyup", self._handleKeyEvent);
+ }
+
+ /**
+ * binds an event to itsatrap
+ *
+ * can be a single key, a combination of keys separated with +,
+ * an array of keys, or a sequence of keys separated by spaces
+ *
+ * be sure to list the modifier keys first to make sure that the
+ * correct key ends up getting bound (the last key in the pattern)
+ *
+ * @param {string|Array} keys
+ * @param {Function} callback
+ * @param {string=} action - 'keypress', 'keydown', or 'keyup'
+ * @returns void
+ */
+ ItsATrap.prototype.bind = function(keys, callback, action) {
+ let self = this;
+ keys = keys instanceof Array ? keys : [keys];
+ self._bindMultiple.call(self, keys, callback, action);
+ return self;
+ };
+
+ /**
+ * unbinds an event to itsatrap
+ *
+ * the unbinding sets the callback function of the specified key combo
+ * to an empty function and deletes the corresponding key in the
+ * _directMap dict.
+ *
+ * TODO: actually remove this from the _callbacks dictionary instead
+ * of binding an empty function
+ *
+ * the keycombo+action has to be exactly the same as
+ * it was defined in the bind method
+ *
+ * @param {string|Array} keys
+ * @param {string} action
+ * @returns void
+ */
+ ItsATrap.prototype.unbind = function(keys, action) {
+ let self = this;
+ return self.bind.call(self, keys, function() {}, action);
+ };
+
+ /**
+ * triggers an event that has already been bound
+ *
+ * @param {string} keys
+ * @param {string=} action
+ * @returns void
+ */
+ ItsATrap.prototype.trigger = function(keys, action) {
+ let self = this;
+ if (self._directMap[keys + ":" + action]) {
+ self._directMap[keys + ":" + action]({}, keys);
+ }
+ return self;
+ };
+
+ /**
+ * resets the library back to its initial state. this is useful
+ * if you want to clear out the current keyboard shortcuts and bind
+ * new ones - for example if you switch to another page
+ *
+ * @returns void
+ */
+ ItsATrap.prototype.reset = function() {
+ let self = this;
+ self._callbacks = {};
+ self._directMap = {};
+ return self;
+ };
+
+ /**
+ * destroy itsatrap object
+ *
+ * - call reset on the itsatrap object ( removing all binding )
+ * - remove all javascript event listener from target element or document
+ * - remove all reference to target
+ *
+ * @return void
+ */
+
+ ItsATrap.prototype.destroy = function() {
+ let self = this;
+
+ self.reset();
+
+ _removeEvent(self.target, "keypress", self._handleKeyEvent);
+ _removeEvent(self.target, "keydown", self._handleKeyEvent);
+ _removeEvent(self.target, "keyup", self._handleKeyEvent);
+
+ self.target = undefined;
+ self._handleKeyEvent = undefined;
+ };
+
+ /**
+ * should we stop this event before firing off callbacks
+ *
+ * @param {Event} e
+ * @param {Element} element
+ * @return {boolean}
+ */
+ ItsATrap.prototype.stopCallback = function(e, element, combo, sequence) {
+ let self = this;
+
+ if (self.paused) {
+ return true;
+ }
+
+ if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
+ return false;
+ }
+
+ // if the element has the class "itsatrap" then no need to stop
+ if ((" " + element.className + " ").indexOf(" itsatrap ") > -1) {
+ return false;
+ }
+
+ if (_belongsTo(element, self.target)) {
+ return false;
+ }
+
+ // Events originating from a shadow DOM are re-targetted and `e.target` is the shadow host,
+ // not the initial event target in the shadow tree. Note that not all events cross the
+ // shadow boundary.
+ // For shadow trees with `mode: 'open'`, the initial event target is the first element in
+ // the event’s composed path. For shadow trees with `mode: 'closed'`, the initial event
+ // target cannot be obtained.
+ if ("composedPath" in e && typeof e.composedPath === "function") {
+ // For open shadow trees, update `element` so that the following check works.
+ let initialEventTarget = e.composedPath()[0];
+ if (initialEventTarget !== e.target) {
+ element = initialEventTarget;
+ }
+ }
+
+ // stop for input, select, and textarea
+ return (
+ element.tagName == "INPUT" ||
+ element.tagName == "SELECT" ||
+ element.tagName == "TEXTAREA" ||
+ element.isContentEditable
+ );
+ };
+
+ /**
+ * exposes _handleKey publicly so it can be overwritten by extensions
+ */
+ ItsATrap.prototype.handleKey = function() {
+ let self = this;
+ return self._handleKey.apply(self, arguments);
+ };
+
+ /**
+ * allow custom key mappings
+ */
+ ItsATrap.addKeycodes = function(object) {
+ for (let key in object) {
+ if (object.hasOwnProperty(key)) {
+ _MAP[key] = object[key];
+ }
+ }
+ _REVERSE_MAP = null;
+ };
+
+ /**
+ * adds a pause and unpause method to ItsATrap
+ * this allows you to enable or disable keyboard shortcuts
+ * without having to reset ItsATrap and rebind everything
+ */
+ ItsATrap.prototype.pause = function() {
+ let self = this;
+ self.paused = true;
+ };
+
+ ItsATrap.prototype.unpause = function() {
+ let self = this;
+ self.paused = false;
+ };
+
+ /**
+ * adds a bindGlobal method to ItsATrap that allows you to
+ * bind specific keyboard shortcuts that will still work
+ * inside a text input field
+ *
+ * usage:
+ * ItsATrap.bindGlobal('ctrl+s', _saveChanges);
+ */
+ ItsATrap.prototype.bindGlobal = function(keys, callback, action) {
+ let self = this;
+ self.bind(keys, callback, action);
+
+ if (keys instanceof Array) {
+ for (let i = 0; i < keys.length; i++) {
+ _globalCallbacks[keys[i]] = true;
+ }
+ return;
+ }
+
+ _globalCallbacks[keys] = true;
+ };
+
+ // expose itsatrap to the global object
+ window.ItsATrap = ItsATrap;
+
+ // expose as a common js module
+ if (typeof module !== "undefined" && module.exports) {
+ module.exports = ItsATrap;
+ }
+
+ // expose itsatrap as an AMD module
+ if (typeof define === "function" && define.amd) {
+ define(function() {
+ return ItsATrap;
+ });
+ }
+})(
+ typeof window !== "undefined" ? window : null,
+ typeof window !== "undefined" ? document : null
+);
diff --git a/assets/javascripts/legacy/jquery.js b/assets/javascripts/legacy/jquery.js
new file mode 100644
index 00000000..956c6699
--- /dev/null
+++ b/assets/javascripts/legacy/jquery.js
@@ -0,0 +1,10874 @@
+// discourse-skip-module
+
+/*!
+ * jQuery JavaScript Library v3.5.1
+ * https://jquery.com/
+ *
+ * Includes Sizzle.js
+ * https://sizzlejs.com/
+ *
+ * Copyright JS Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2020-05-04T22:49Z
+ */
+( function( global, factory ) {
+
+ "use strict";
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+// enough that all such attempts are guarded in a try block.
+"use strict";
+
+let arr = [];
+
+let getProto = Object.getPrototypeOf;
+
+let slice = arr.slice;
+
+let flat = arr.flat ? function( array ) {
+ return arr.flat.call( array );
+} : function( array ) {
+ return arr.concat.apply( [], array );
+};
+
+
+let push = arr.push;
+
+let indexOf = arr.indexOf;
+
+let class2type = {};
+
+let toString = class2type.toString;
+
+let hasOwn = class2type.hasOwnProperty;
+
+let fnToString = hasOwn.toString;
+
+let ObjectFunctionString = fnToString.call( Object );
+
+let support = {};
+
+let isFunction = function isFunction( obj ) {
+
+ // Support: Chrome <=57, Firefox <=52
+ // In some browsers, typeof returns "function" for HTML