').appendTo(this.$container);\n\n this.resizeDragger = new Garnish.BaseDrag($resizeDragHandle, {\n onDragStart: this._handleResizeStart.bind(this),\n onDrag: this._handleResize.bind(this)\n });\n }\n\n this.addListener(this.$container, 'click', function(ev) {\n ev.stopPropagation();\n });\n\n // Show it if we're late to the party\n if (this.visible) {\n this.show();\n }\n },\n\n show: function() {\n // Close other modals as needed\n if (this.settings.closeOtherModals && Garnish.Modal.visibleModal && Garnish.Modal.visibleModal !== this) {\n Garnish.Modal.visibleModal.hide();\n }\n\n if (this.$container) {\n // Move it to the end of so it gets the highest sub-z-index\n this.$shade.appendTo(Garnish.$bod);\n this.$container.appendTo(Garnish.$bod);\n\n this.$container.show();\n this.updateSizeAndPosition();\n\n this.$shade.velocity('fadeIn', {\n duration: 50,\n complete: function() {\n this.$container.velocity('fadeIn', {\n complete: function() {\n this.updateSizeAndPosition();\n this.onFadeIn();\n }.bind(this)\n });\n }.bind(this)\n });\n\n if (this.settings.hideOnShadeClick) {\n this.addListener(this.$shade, 'click', 'hide');\n }\n\n this.addListener(Garnish.$win, 'resize', '_handleWindowResize');\n }\n\n this.enable();\n\n if (!this.visible) {\n this.visible = true;\n Garnish.Modal.visibleModal = this;\n\n Garnish.shortcutManager.addLayer();\n\n if (this.settings.hideOnEsc) {\n Garnish.shortcutManager.registerShortcut(Garnish.ESC_KEY, this.hide.bind(this));\n }\n\n this.trigger('show');\n this.settings.onShow();\n }\n },\n\n quickShow: function() {\n this.show();\n\n if (this.$container) {\n this.$container.velocity('stop');\n this.$container.show().css('opacity', 1);\n\n this.$shade.velocity('stop');\n this.$shade.show().css('opacity', 1);\n }\n },\n\n hide: function(ev) {\n if (!this.visible) {\n return;\n }\n\n this.disable();\n\n if (ev) {\n ev.stopPropagation();\n }\n\n if (this.$container) {\n this.$container.velocity('fadeOut', {duration: Garnish.FX_DURATION});\n this.$shade.velocity('fadeOut', {\n duration: Garnish.FX_DURATION,\n complete: this.onFadeOut.bind(this)\n });\n\n if (this.settings.hideOnShadeClick) {\n this.removeListener(this.$shade, 'click');\n }\n\n this.removeListener(Garnish.$win, 'resize');\n }\n\n this.visible = false;\n Garnish.Modal.visibleModal = null;\n Garnish.shortcutManager.removeLayer();\n this.trigger('hide');\n this.settings.onHide();\n },\n\n quickHide: function() {\n this.hide();\n\n if (this.$container) {\n this.$container.velocity('stop');\n this.$container.css('opacity', 0).hide();\n\n this.$shade.velocity('stop');\n this.$shade.css('opacity', 0).hide();\n }\n },\n\n updateSizeAndPosition: function() {\n if (!this.$container) {\n return;\n }\n\n this.$container.css({\n 'width': (this.desiredWidth ? Math.max(this.desiredWidth, 200) : ''),\n 'height': (this.desiredHeight ? Math.max(this.desiredHeight, 200) : ''),\n 'min-width': '',\n 'min-height': ''\n });\n\n // Set the width first so that the height can adjust for the width\n this.updateSizeAndPosition._windowWidth = Garnish.$win.width();\n this.updateSizeAndPosition._width = Math.min(this.getWidth(), this.updateSizeAndPosition._windowWidth - this.settings.minGutter * 2);\n\n this.$container.css({\n 'width': this.updateSizeAndPosition._width,\n 'min-width': this.updateSizeAndPosition._width,\n 'left': Math.round((this.updateSizeAndPosition._windowWidth - this.updateSizeAndPosition._width) / 2)\n });\n\n // Now set the height\n this.updateSizeAndPosition._windowHeight = Garnish.$win.height();\n this.updateSizeAndPosition._height = Math.min(this.getHeight(), this.updateSizeAndPosition._windowHeight - this.settings.minGutter * 2);\n\n this.$container.css({\n 'height': this.updateSizeAndPosition._height,\n 'min-height': this.updateSizeAndPosition._height,\n 'top': Math.round((this.updateSizeAndPosition._windowHeight - this.updateSizeAndPosition._height) / 2)\n });\n\n this.trigger('updateSizeAndPosition');\n },\n\n onFadeIn: function() {\n this.trigger('fadeIn');\n this.settings.onFadeIn();\n },\n\n onFadeOut: function() {\n this.trigger('fadeOut');\n this.settings.onFadeOut();\n },\n\n getHeight: function() {\n if (!this.$container) {\n throw 'Attempted to get the height of a modal whose container has not been set.';\n }\n\n if (!this.visible) {\n this.$container.show();\n }\n\n this.getHeight._height = this.$container.outerHeight();\n\n if (!this.visible) {\n this.$container.hide();\n }\n\n return this.getHeight._height;\n },\n\n getWidth: function() {\n if (!this.$container) {\n throw 'Attempted to get the width of a modal whose container has not been set.';\n }\n\n if (!this.visible) {\n this.$container.show();\n }\n\n // Chrome might be 1px shy here for some reason\n this.getWidth._width = this.$container.outerWidth() + 1;\n\n if (!this.visible) {\n this.$container.hide();\n }\n\n return this.getWidth._width;\n },\n\n _handleWindowResize: function(ev) {\n // ignore propagated resize events\n if (ev.target === window) {\n this.updateSizeAndPosition();\n }\n },\n\n _handleResizeStart: function() {\n this.resizeStartWidth = this.getWidth();\n this.resizeStartHeight = this.getHeight();\n },\n\n _handleResize: function() {\n if (Garnish.ltr) {\n this.desiredWidth = this.resizeStartWidth + (this.resizeDragger.mouseDistX * 2);\n }\n else {\n this.desiredWidth = this.resizeStartWidth - (this.resizeDragger.mouseDistX * 2);\n }\n\n this.desiredHeight = this.resizeStartHeight + (this.resizeDragger.mouseDistY * 2);\n\n this.updateSizeAndPosition();\n },\n\n /**\n * Destroy\n */\n destroy: function() {\n if (this.$container) {\n this.$container.removeData('modal').remove();\n }\n\n if (this.$shade) {\n this.$shade.remove();\n }\n\n if (this.dragger) {\n this.dragger.destroy();\n }\n\n if (this.resizeDragger) {\n this.resizeDragger.destroy();\n }\n\n this.base();\n }\n },\n {\n relativeElemPadding: 8,\n defaults: {\n autoShow: true,\n draggable: false,\n dragHandleSelector: null,\n resizable: false,\n minGutter: 10,\n onShow: $.noop,\n onHide: $.noop,\n onFadeIn: $.noop,\n onFadeOut: $.noop,\n closeOtherModals: false,\n hideOnEsc: true,\n hideOnShadeClick: true,\n shadeClass: 'modal-shade'\n },\n instances: [],\n visibleModal: null\n }\n);\n","import Garnish from './Garnish.js';\nimport Base from './Base.js';\nimport $ from 'jquery';\n\n/**\n * Nice Text\n */\nexport default Base.extend(\n {\n $input: null,\n $hint: null,\n $stage: null,\n $charsLeft: null,\n autoHeight: null,\n maxLength: null,\n showCharsLeft: false,\n showingHint: false,\n val: null,\n inputBoxSizing: 'content-box',\n width: null,\n height: null,\n minHeight: null,\n initialized: false,\n\n init: function(input, settings) {\n this.$input = $(input);\n this.settings = $.extend({}, Garnish.NiceText.defaults, settings);\n\n if (this.isVisible()) {\n this.initialize();\n }\n else {\n this.addListener(Garnish.$win, 'resize', 'initializeIfVisible');\n }\n },\n\n isVisible: function() {\n return (this.$input.height() > 0);\n },\n\n initialize: function() {\n if (this.initialized) {\n return;\n }\n\n this.initialized = true;\n this.removeListener(Garnish.$win, 'resize');\n\n this.maxLength = this.$input.attr('maxlength');\n\n if (this.maxLength) {\n this.maxLength = parseInt(this.maxLength);\n }\n\n if (this.maxLength && (this.settings.showCharsLeft || Garnish.hasAttr(this.$input, 'data-show-chars-left'))) {\n this.showCharsLeft = true;\n\n // Remove the maxlength attribute\n this.$input.removeAttr('maxlength');\n }\n\n // Is this already a transparent text input?\n if (this.$input.data('nicetext')) {\n Garnish.log('Double-instantiating a transparent text input on an element');\n this.$input.data('nicetext').destroy();\n }\n\n this.$input.data('nicetext', this);\n\n this.getVal();\n\n this.autoHeight = (this.settings.autoHeight && this.$input.prop('nodeName') === 'TEXTAREA');\n\n if (this.autoHeight) {\n this.minHeight = this.getHeightForValue('');\n this.updateHeight();\n\n // Update height when the window resizes\n this.width = this.$input.width();\n this.addListener(Garnish.$win, 'resize', 'updateHeightIfWidthChanged');\n }\n\n if (this.settings.hint) {\n this.$hintContainer = $('
').insertBefore(this.$input);\n this.$hint = $('
' + this.settings.hint + '
').appendTo(this.$hintContainer);\n this.$hint.css({\n top: (parseInt(this.$input.css('borderTopWidth')) + parseInt(this.$input.css('paddingTop'))),\n left: (parseInt(this.$input.css('borderLeftWidth')) + parseInt(this.$input.css('paddingLeft')) + 1)\n });\n Garnish.copyTextStyles(this.$input, this.$hint);\n\n if (this.val) {\n this.$hint.hide();\n }\n else {\n this.showingHint = true;\n }\n\n // Focus the input when clicking on the hint\n this.addListener(this.$hint, 'mousedown', function(ev) {\n ev.preventDefault();\n this.$input.focus();\n });\n }\n\n if (this.showCharsLeft) {\n this.$charsLeft = $('
').insertAfter(this.$input);\n this.updateCharsLeft();\n }\n\n this.addListener(this.$input, 'textchange', 'onTextChange');\n this.addListener(this.$input, 'keydown', 'onKeyDown');\n },\n\n initializeIfVisible: function() {\n if (this.isVisible()) {\n this.initialize();\n }\n },\n\n getVal: function() {\n this.val = this.$input.val();\n return this.val;\n },\n\n showHint: function() {\n this.$hint.velocity('fadeIn', {\n complete: Garnish.NiceText.hintFadeDuration\n });\n\n this.showingHint = true;\n },\n\n hideHint: function() {\n this.$hint.velocity('fadeOut', {\n complete: Garnish.NiceText.hintFadeDuration\n });\n\n this.showingHint = false;\n },\n\n onTextChange: function() {\n this.getVal();\n\n if (this.$hint) {\n if (this.showingHint && this.val) {\n this.hideHint();\n }\n else if (!this.showingHint && !this.val) {\n this.showHint();\n }\n }\n\n if (this.autoHeight) {\n this.updateHeight();\n }\n\n if (this.showCharsLeft) {\n this.updateCharsLeft();\n }\n },\n\n onKeyDown: function(ev) {\n // If Ctrl/Command + Return is pressed, submit the closest form\n if (ev.keyCode === Garnish.RETURN_KEY && Garnish.isCtrlKeyPressed(ev)) {\n ev.preventDefault();\n this.$input.closest('form').submit();\n }\n },\n\n buildStage: function() {\n this.$stage = $('
').appendTo(Garnish.$bod);\n\n // replicate the textarea's text styles\n this.$stage.css({\n display: 'block',\n position: 'absolute',\n top: -9999,\n left: -9999\n });\n\n this.inputBoxSizing = this.$input.css('box-sizing');\n\n if (this.inputBoxSizing === 'border-box') {\n this.$stage.css({\n 'border-top': this.$input.css('border-top'),\n 'border-right': this.$input.css('border-right'),\n 'border-bottom': this.$input.css('border-bottom'),\n 'border-left': this.$input.css('border-left'),\n 'padding-top': this.$input.css('padding-top'),\n 'padding-right': this.$input.css('padding-right'),\n 'padding-bottom': this.$input.css('padding-bottom'),\n 'padding-left': this.$input.css('padding-left'),\n '-webkit-box-sizing': this.inputBoxSizing,\n '-moz-box-sizing': this.inputBoxSizing,\n 'box-sizing': this.inputBoxSizing\n });\n }\n\n Garnish.copyTextStyles(this.$input, this.$stage);\n },\n\n getHeightForValue: function(val) {\n if (!this.$stage) {\n this.buildStage();\n }\n\n if (this.inputBoxSizing === 'border-box') {\n this.$stage.css('width', this.$input.outerWidth());\n }\n else {\n this.$stage.css('width', this.$input.width());\n }\n\n if (!val) {\n val = ' ';\n for (var i = 1; i < this.$input.prop('rows'); i++) {\n val += '
';\n }\n }\n else {\n // Ampersand entities\n val = val.replace(/&/g, '&');\n\n // < and >\n val = val.replace(//g, '>');\n\n // Multiple spaces\n val = val.replace(/ {2,}/g, function(spaces) {\n // TODO: replace with String.repeat() when more broadly available?\n var replace = '';\n for (var i = 0; i < spaces.length - 1; i++) {\n replace += ' ';\n }\n return replace + ' ';\n });\n\n // Line breaks\n val = val.replace(/[\\n\\r]$/g, '
');\n val = val.replace(/[\\n\\r]/g, '
');\n }\n\n this.$stage.html(val);\n\n if (this.inputBoxSizing === 'border-box') {\n this.getHeightForValue._height = this.$stage.outerHeight();\n }\n else {\n this.getHeightForValue._height = this.$stage.height();\n }\n\n if (this.minHeight && this.getHeightForValue._height < this.minHeight) {\n this.getHeightForValue._height = this.minHeight;\n }\n\n return this.getHeightForValue._height;\n },\n\n updateHeight: function() {\n // has the height changed?\n if (this.height !== (this.height = this.getHeightForValue(this.val))) {\n this.$input.css('min-height', this.height);\n\n if (this.initialized) {\n this.onHeightChange();\n }\n }\n },\n\n updateHeightIfWidthChanged: function() {\n if (this.isVisible() && this.width !== (this.width = this.$input.width()) && this.width) {\n this.updateHeight();\n }\n },\n\n onHeightChange: function() {\n this.settings.onHeightChange();\n },\n\n updateCharsLeft: function() {\n this.updateCharsLeft._charsLeft = this.maxLength - this.val.length;\n this.$charsLeft.html(Garnish.NiceText.charsLeftHtml(this.updateCharsLeft._charsLeft));\n\n if (this.updateCharsLeft._charsLeft >= 0) {\n this.$charsLeft.removeClass(this.settings.negativeCharsLeftClass);\n }\n else {\n this.$charsLeft.addClass(this.settings.negativeCharsLeftClass);\n }\n },\n\n /**\n * Destroy\n */\n destroy: function() {\n this.$input.removeData('nicetext');\n\n if (this.$hint) {\n this.$hint.remove();\n }\n\n if (this.$stage) {\n this.$stage.remove();\n }\n\n this.base();\n }\n },\n {\n interval: 100,\n hintFadeDuration: 50,\n charsLeftHtml: function(charsLeft) {\n return charsLeft;\n },\n defaults: {\n autoHeight: true,\n showCharsLeft: false,\n charsLeftClass: 'chars-left',\n negativeCharsLeftClass: 'negative-chars-left',\n onHeightChange: $.noop\n }\n }\n);\n","import Garnish from './Garnish.js';\nimport Base from './Base.js';\nimport $ from 'jquery';\n\n/**\n * Select\n */\nexport default Base.extend(\n {\n $container: null,\n $items: null,\n $selectedItems: null,\n $focusedItem: null,\n\n mousedownTarget: null,\n mouseUpTimeout: null,\n callbackFrame: null,\n\n $focusable: null,\n $first: null,\n first: null,\n $last: null,\n last: null,\n\n /**\n * Constructor\n */\n init: function(container, items, settings) {\n this.$container = $(container);\n\n // Param mapping\n if (typeof items === 'undefined' && $.isPlainObject(container)) {\n // (settings)\n settings = container;\n container = null;\n items = null;\n }\n else if (typeof settings === 'undefined' && $.isPlainObject(items)) {\n // (container, settings)\n settings = items;\n items = null;\n }\n\n // Is this already a select?\n if (this.$container.data('select')) {\n Garnish.log('Double-instantiating a select on an element');\n this.$container.data('select').destroy();\n }\n\n this.$container.data('select', this);\n\n this.setSettings(settings, Garnish.Select.defaults);\n\n this.$items = $();\n this.$selectedItems = $();\n\n this.addItems(items);\n\n // --------------------------------------------------------------------\n\n if (this.settings.allowEmpty && !this.settings.checkboxMode) {\n this.addListener(this.$container, 'click', function() {\n if (this.ignoreClick) {\n this.ignoreClick = false;\n }\n else {\n // Deselect all items on container click\n this.deselectAll(true);\n }\n });\n }\n },\n\n /**\n * Get Item Index\n */\n getItemIndex: function($item) {\n return this.$items.index($item[0]);\n },\n\n /**\n * Is Selected?\n */\n isSelected: function(item) {\n if (Garnish.isJquery(item)) {\n if (!item[0]) {\n return false;\n }\n\n item = item[0];\n }\n\n return ($.inArray(item, this.$selectedItems) !== -1);\n },\n\n /**\n * Select Item\n */\n selectItem: function($item, focus, preventScroll) {\n if (!this.settings.multi) {\n this.deselectAll();\n }\n\n this.$first = this.$last = $item;\n this.first = this.last = this.getItemIndex($item);\n\n if (focus) {\n this.setFocusableItem($item);\n this.focusItem($item, preventScroll);\n }\n\n this._selectItems($item);\n },\n\n selectAll: function() {\n if (!this.settings.multi || !this.$items.length) {\n return;\n }\n\n this.first = 0;\n this.last = this.$items.length - 1;\n this.$first = this.$items.eq(this.first);\n this.$last = this.$items.eq(this.last);\n\n this._selectItems(this.$items);\n },\n\n /**\n * Select Range\n */\n selectRange: function($item, preventScroll) {\n if (!this.settings.multi) {\n return this.selectItem($item, true, true);\n }\n\n this.deselectAll();\n\n this.$last = $item;\n this.last = this.getItemIndex($item);\n\n this.setFocusableItem($item);\n this.focusItem($item, preventScroll);\n\n // prepare params for $.slice()\n var sliceFrom, sliceTo;\n\n if (this.first < this.last) {\n sliceFrom = this.first;\n sliceTo = this.last + 1;\n }\n else {\n sliceFrom = this.last;\n sliceTo = this.first + 1;\n }\n\n this._selectItems(this.$items.slice(sliceFrom, sliceTo));\n },\n\n /**\n * Deselect Item\n */\n deselectItem: function($item) {\n var index = this.getItemIndex($item);\n if (this.first === index) {\n this.$first = this.first = null;\n }\n if (this.last === index) {\n this.$last = this.last = null;\n }\n\n this._deselectItems($item);\n },\n\n /**\n * Deselect All\n */\n deselectAll: function(clearFirst) {\n if (clearFirst) {\n this.$first = this.first = this.$last = this.last = null;\n }\n\n this._deselectItems(this.$items);\n },\n\n /**\n * Deselect Others\n */\n deselectOthers: function($item) {\n this.deselectAll();\n this.selectItem($item, true, true);\n },\n\n /**\n * Toggle Item\n */\n toggleItem: function($item, preventScroll) {\n if (!this.isSelected($item)) {\n this.selectItem($item, true, preventScroll);\n }\n else {\n if (this._canDeselect($item)) {\n this.deselectItem($item, true);\n }\n }\n },\n\n clearMouseUpTimeout: function() {\n clearTimeout(this.mouseUpTimeout);\n },\n\n getFirstItem: function() {\n if (this.$items.length) {\n return this.$items.first();\n }\n },\n\n getLastItem: function() {\n if (this.$items.length) {\n return this.$items.last();\n }\n },\n\n isPreviousItem: function(index) {\n return (index > 0);\n },\n\n isNextItem: function(index) {\n return (index < this.$items.length - 1);\n },\n\n getPreviousItem: function(index) {\n if (this.isPreviousItem(index)) {\n return this.$items.eq(index - 1);\n }\n },\n\n getNextItem: function(index) {\n if (this.isNextItem(index)) {\n return this.$items.eq(index + 1);\n }\n },\n\n getItemToTheLeft: function(index) {\n var func = (Garnish.ltr ? 'Previous' : 'Next');\n\n if (this['is' + func + 'Item'](index)) {\n if (this.settings.horizontal) {\n return this['get' + func + 'Item'](index);\n }\n if (!this.settings.vertical) {\n return this.getClosestItem(index, Garnish.X_AXIS, '<');\n }\n }\n },\n\n getItemToTheRight: function(index) {\n var func = (Garnish.ltr ? 'Next' : 'Previous');\n\n if (this['is' + func + 'Item'](index)) {\n if (this.settings.horizontal) {\n return this['get' + func + 'Item'](index);\n }\n else if (!this.settings.vertical) {\n return this.getClosestItem(index, Garnish.X_AXIS, '>');\n }\n }\n },\n\n getItemAbove: function(index) {\n if (this.isPreviousItem(index)) {\n if (this.settings.vertical) {\n return this.getPreviousItem(index);\n }\n else if (!this.settings.horizontal) {\n return this.getClosestItem(index, Garnish.Y_AXIS, '<');\n }\n }\n },\n\n getItemBelow: function(index) {\n if (this.isNextItem(index)) {\n if (this.settings.vertical) {\n return this.getNextItem(index);\n }\n else if (!this.settings.horizontal) {\n return this.getClosestItem(index, Garnish.Y_AXIS, '>');\n }\n }\n },\n\n getClosestItem: function(index, axis, dir) {\n var axisProps = Garnish.Select.closestItemAxisProps[axis],\n dirProps = Garnish.Select.closestItemDirectionProps[dir];\n\n var $thisItem = this.$items.eq(index),\n thisOffset = $thisItem.offset(),\n thisMidpoint = thisOffset[axisProps.midpointOffset] + Math.round($thisItem[axisProps.midpointSizeFunc]() / 2),\n otherRowPos = null,\n smallestMidpointDiff = null,\n $closestItem = null;\n\n // Go the other way if this is the X axis and a RTL page\n var step;\n\n if (Garnish.rtl && axis === Garnish.X_AXIS) {\n step = dirProps.step * -1;\n }\n else {\n step = dirProps.step;\n }\n\n for (var i = index + step; (typeof this.$items[i] !== 'undefined'); i += step) {\n var $otherItem = this.$items.eq(i),\n otherOffset = $otherItem.offset();\n\n // Are we on the next row yet?\n if (dirProps.isNextRow(otherOffset[axisProps.rowOffset], thisOffset[axisProps.rowOffset])) {\n // Is this the first time we've seen this row?\n if (otherRowPos === null) {\n otherRowPos = otherOffset[axisProps.rowOffset];\n }\n // Have we gone too far?\n else if (otherOffset[axisProps.rowOffset] !== otherRowPos) {\n break;\n }\n\n var otherMidpoint = otherOffset[axisProps.midpointOffset] + Math.round($otherItem[axisProps.midpointSizeFunc]() / 2),\n midpointDiff = Math.abs(thisMidpoint - otherMidpoint);\n\n // Are we getting warmer?\n if (smallestMidpointDiff === null || midpointDiff < smallestMidpointDiff) {\n smallestMidpointDiff = midpointDiff;\n $closestItem = $otherItem;\n }\n // Getting colder?\n else {\n break;\n }\n }\n // Getting colder?\n else if (dirProps.isWrongDirection(otherOffset[axisProps.rowOffset], thisOffset[axisProps.rowOffset])) {\n break;\n }\n }\n\n return $closestItem;\n },\n\n getFurthestItemToTheLeft: function(index) {\n return this.getFurthestItem(index, 'ToTheLeft');\n },\n\n getFurthestItemToTheRight: function(index) {\n return this.getFurthestItem(index, 'ToTheRight');\n },\n\n getFurthestItemAbove: function(index) {\n return this.getFurthestItem(index, 'Above');\n },\n\n getFurthestItemBelow: function(index) {\n return this.getFurthestItem(index, 'Below');\n },\n\n getFurthestItem: function(index, dir) {\n var $item, $testItem;\n\n while ($testItem = this['getItem' + dir](index)) {\n $item = $testItem;\n index = this.getItemIndex($item);\n }\n\n return $item;\n },\n\n /**\n * totalSelected getter\n */\n get totalSelected() {\n return this.getTotalSelected();\n },\n\n /**\n * Get Total Selected\n */\n getTotalSelected: function() {\n return this.$selectedItems.length;\n },\n\n /**\n * Add Items\n */\n addItems: function(items) {\n var $items = $(items);\n\n for (var i = 0; i < $items.length; i++) {\n var item = $items[i];\n\n // Make sure this element doesn't belong to another selector\n if ($.data(item, 'select')) {\n Garnish.log('Element was added to more than one selector');\n $.data(item, 'select').removeItems(item);\n }\n\n // Add the item\n $.data(item, 'select', this);\n\n // Get the handle\n var $handle;\n\n if (this.settings.handle) {\n if (typeof this.settings.handle === 'object') {\n $handle = $(this.settings.handle);\n }\n else if (typeof this.settings.handle === 'string') {\n $handle = $(item).find(this.settings.handle);\n }\n else if (typeof this.settings.handle === 'function') {\n $handle = $(this.settings.handle(item));\n }\n }\n else {\n $handle = $(item);\n }\n\n $.data(item, 'select-handle', $handle);\n $handle.data('select-item', item);\n\n this.addListener($handle, 'mousedown', 'onMouseDown');\n this.addListener($handle, 'mouseup', 'onMouseUp');\n this.addListener($handle, 'click', function() {\n this.ignoreClick = true;\n });\n\n this.addListener(item, 'keydown', 'onKeyDown');\n }\n\n this.$items = this.$items.add($items);\n this.updateIndexes();\n },\n\n /**\n * Remove Items\n */\n removeItems: function(items) {\n items = $.makeArray(items);\n\n var itemsChanged = false,\n selectionChanged = false;\n\n for (var i = 0; i < items.length; i++) {\n var item = items[i];\n\n // Make sure we actually know about this item\n var index = $.inArray(item, this.$items);\n if (index !== -1) {\n this._deinitItem(item);\n this.$items.splice(index, 1);\n itemsChanged = true;\n\n var selectedIndex = $.inArray(item, this.$selectedItems);\n if (selectedIndex !== -1) {\n this.$selectedItems.splice(selectedIndex, 1);\n selectionChanged = true;\n }\n }\n }\n\n if (itemsChanged) {\n this.updateIndexes();\n\n if (selectionChanged) {\n $(items).removeClass(this.settings.selectedClass);\n this.onSelectionChange();\n }\n }\n },\n\n /**\n * Remove All Items\n */\n removeAllItems: function() {\n for (var i = 0; i < this.$items.length; i++) {\n this._deinitItem(this.$items[i]);\n }\n\n this.$items = $();\n this.$selectedItems = $();\n this.updateIndexes();\n },\n\n /**\n * Update First/Last indexes\n */\n updateIndexes: function() {\n if (this.first !== null) {\n this.first = this.getItemIndex(this.$first);\n this.setFocusableItem(this.$first);\n }\n else if (this.$items.length) {\n this.setFocusableItem($(this.$items[0]));\n }\n\n if (this.$focusedItem) {\n this.setFocusableItem(this.$focusedItem);\n this.focusItem(this.$focusedItem, true);\n }\n\n if (this.last !== null) {\n this.last = this.getItemIndex(this.$last);\n }\n },\n\n /**\n * Reset Item Order\n */\n resetItemOrder: function() {\n this.$items = $().add(this.$items);\n this.$selectedItems = $().add(this.$selectedItems);\n this.updateIndexes();\n },\n\n /**\n * Sets the focusable item.\n *\n * We only want to have one focusable item per selection list, so that the user\n * doesn't have to tab through a million items.\n *\n * @param {object} $item\n */\n setFocusableItem: function($item) {\n if (this.$focusable) {\n this.$focusable.removeAttr('tabindex');\n }\n\n this.$focusable = $item.attr('tabindex', '0');\n },\n\n /**\n * Sets the focus on an item.\n */\n focusItem: function($item, preventScroll) {\n $item[0].focus({preventScroll: !!preventScroll});\n this.$focusedItem = $item;\n this.trigger('focusItem', {item: $item});\n },\n\n /**\n * Get Selected Items\n */\n getSelectedItems: function() {\n return this.$selectedItems;\n },\n\n /**\n * Destroy\n */\n destroy: function() {\n this.$container.removeData('select');\n this.removeAllItems();\n this.base();\n },\n\n // Events\n // ---------------------------------------------------------------------\n\n /**\n * On Mouse Down\n */\n onMouseDown: function(ev) {\n // ignore right clicks\n if (ev.which !== Garnish.PRIMARY_CLICK) {\n return;\n }\n\n // Enforce the filter\n if (this.settings.filter && !$(ev.target).is(this.settings.filter)) {\n return;\n }\n\n this.mousedownTarget = ev.currentTarget;\n\n var $item = $($.data(ev.currentTarget, 'select-item'));\n\n if (this.first !== null && ev.shiftKey) {\n // Shift key is consistent for both selection modes\n this.selectRange($item, true);\n }\n else if (this._actAsCheckbox(ev)) {\n this.toggleItem($item, true);\n }\n },\n\n /**\n * On Mouse Up\n */\n onMouseUp: function(ev) {\n // ignore right clicks\n if (ev.which !== Garnish.PRIMARY_CLICK) {\n return;\n }\n\n // Enfore the filter\n if (this.settings.filter && !$(ev.target).is(this.settings.filter)) {\n return;\n }\n\n var $item = $($.data(ev.currentTarget, 'select-item'));\n\n // was this a click?\n if (\n !this._actAsCheckbox(ev) && !ev.shiftKey &&\n ev.currentTarget === this.mousedownTarget\n ) {\n // If this is already selected, wait a moment to see if this is a double click before making any rash decisions\n if (this.isSelected($item)) {\n this.clearMouseUpTimeout();\n\n this.mouseUpTimeout = setTimeout(function() {\n this.deselectOthers($item);\n }.bind(this), 300);\n }\n else {\n this.deselectAll();\n this.selectItem($item, true, true);\n }\n }\n },\n\n /**\n * On Key Down\n */\n onKeyDown: function(ev) {\n // Ignore if the focus isn't on one of our items\n if (ev.target !== ev.currentTarget) {\n return;\n }\n\n var ctrlKey = Garnish.isCtrlKeyPressed(ev);\n var shiftKey = ev.shiftKey;\n\n var anchor, $item;\n\n if (!this.settings.checkboxMode || !this.$focusable.length) {\n anchor = ev.shiftKey ? this.last : this.first;\n }\n else {\n anchor = $.inArray(this.$focusable[0], this.$items);\n\n if (anchor === -1) {\n anchor = 0;\n }\n }\n\n // Ok, what are we doing here?\n switch (ev.keyCode) {\n case Garnish.LEFT_KEY: {\n ev.preventDefault();\n\n // Select the last item if none are selected\n if (this.first === null) {\n if (Garnish.ltr) {\n $item = this.getLastItem();\n }\n else {\n $item = this.getFirstItem();\n }\n }\n else {\n if (ctrlKey) {\n $item = this.getFurthestItemToTheLeft(anchor);\n }\n else {\n $item = this.getItemToTheLeft(anchor);\n }\n }\n\n break;\n }\n\n case Garnish.RIGHT_KEY: {\n ev.preventDefault();\n\n // Select the first item if none are selected\n if (this.first === null) {\n if (Garnish.ltr) {\n $item = this.getFirstItem();\n }\n else {\n $item = this.getLastItem();\n }\n }\n else {\n if (ctrlKey) {\n $item = this.getFurthestItemToTheRight(anchor);\n }\n else {\n $item = this.getItemToTheRight(anchor);\n }\n }\n\n break;\n }\n\n case Garnish.UP_KEY: {\n ev.preventDefault();\n\n // Select the last item if none are selected\n if (this.first === null) {\n if (this.$focusable) {\n $item = this.$focusable.prev();\n }\n\n if (!this.$focusable || !$item.length) {\n $item = this.getLastItem();\n }\n }\n else {\n if (ctrlKey) {\n $item = this.getFurthestItemAbove(anchor);\n }\n else {\n $item = this.getItemAbove(anchor);\n }\n\n if (!$item) {\n $item = this.getFirstItem();\n }\n }\n\n break;\n }\n\n case Garnish.DOWN_KEY: {\n ev.preventDefault();\n\n // Select the first item if none are selected\n if (this.first === null) {\n if (this.$focusable) {\n $item = this.$focusable.next();\n }\n\n if (!this.$focusable || !$item.length) {\n $item = this.getFirstItem();\n }\n }\n else {\n if (ctrlKey) {\n $item = this.getFurthestItemBelow(anchor);\n }\n else {\n $item = this.getItemBelow(anchor);\n }\n\n if (!$item) {\n $item = this.getLastItem();\n }\n }\n\n break;\n }\n\n case Garnish.SPACE_KEY: {\n if (!ctrlKey && !shiftKey) {\n ev.preventDefault();\n\n if (this.isSelected(this.$focusable)) {\n if (this._canDeselect(this.$focusable)) {\n this.deselectItem(this.$focusable);\n }\n }\n else {\n this.selectItem(this.$focusable, true, false);\n }\n }\n\n break;\n }\n\n case Garnish.A_KEY: {\n if (ctrlKey) {\n ev.preventDefault();\n this.selectAll();\n }\n\n break;\n }\n }\n\n // Is there an item queued up for focus/selection?\n if ($item && $item.length) {\n if (!this.settings.checkboxMode) {\n // select it\n if (this.first !== null && ev.shiftKey) {\n this.selectRange($item, false);\n }\n else {\n this.deselectAll();\n this.selectItem($item, true, false);\n }\n }\n else {\n // just set the new item to be focusable\n this.setFocusableItem($item);\n $item.focus();\n this.$focusedItem = $item;\n this.trigger('focusItem', {item: $item});\n }\n }\n },\n\n /**\n * Set Callback Timeout\n */\n onSelectionChange: function() {\n if (this.callbackFrame) {\n Garnish.cancelAnimationFrame(this.callbackFrame);\n this.callbackFrame = null;\n }\n\n this.callbackFrame = Garnish.requestAnimationFrame(function() {\n this.callbackFrame = null;\n this.trigger('selectionChange');\n this.settings.onSelectionChange();\n }.bind(this));\n },\n\n // Private methods\n // ---------------------------------------------------------------------\n\n _actAsCheckbox: function(ev) {\n if (Garnish.isCtrlKeyPressed(ev)) {\n return !this.settings.checkboxMode;\n }\n else {\n return this.settings.checkboxMode;\n }\n },\n\n _canDeselect: function($items) {\n return (this.settings.allowEmpty || this.totalSelected > $items.length);\n },\n\n _selectItems: function($items) {\n $items.addClass(this.settings.selectedClass);\n this.$selectedItems = this.$selectedItems.add($items);\n this.onSelectionChange();\n },\n\n _deselectItems: function($items) {\n $items.removeClass(this.settings.selectedClass);\n this.$selectedItems = this.$selectedItems.not($items);\n this.onSelectionChange();\n },\n\n /**\n * Deinitialize an item.\n */\n _deinitItem: function(item) {\n var $handle = $.data(item, 'select-handle');\n\n if ($handle) {\n $handle.removeData('select-item');\n this.removeAllListeners($handle);\n }\n\n $.removeData(item, 'select');\n $.removeData(item, 'select-handle');\n\n if (this.$focusedItem && this.$focusedItem[0] === item) {\n this.$focusedItem = null;\n }\n }\n },\n {\n defaults: {\n selectedClass: 'sel',\n multi: false,\n allowEmpty: true,\n vertical: false,\n horizontal: false,\n handle: null,\n filter: null,\n checkboxMode: false,\n onSelectionChange: $.noop\n },\n\n closestItemAxisProps: {\n x: {\n midpointOffset: 'top',\n midpointSizeFunc: 'outerHeight',\n rowOffset: 'left'\n },\n y: {\n midpointOffset: 'left',\n midpointSizeFunc: 'outerWidth',\n rowOffset: 'top'\n }\n },\n\n closestItemDirectionProps: {\n '<': {\n step: -1,\n isNextRow: function(a, b) {\n return (a < b);\n },\n isWrongDirection: function(a, b) {\n return (a > b);\n }\n },\n '>': {\n step: 1,\n isNextRow: function(a, b) {\n return (a > b);\n },\n isWrongDirection: function(a, b) {\n return (a < b);\n }\n }\n }\n }\n);\n","import Garnish from './Garnish.js';\nimport CustomSelect from './CustomSelect.js';\nimport $ from 'jquery';\n\n/**\n * Select Menu\n */\nexport default CustomSelect.extend(\n {\n /**\n * Constructor\n */\n init: function(btn, options, settings, callback) {\n // argument mapping\n if (typeof settings === 'function') {\n // (btn, options, callback)\n callback = settings;\n settings = {};\n }\n\n settings = $.extend({}, Garnish.SelectMenu.defaults, settings);\n\n this.base(btn, options, settings, callback);\n\n this.selected = -1;\n },\n\n /**\n * Build\n */\n build: function() {\n this.base();\n\n if (this.selected !== -1) {\n this._addSelectedOptionClass(this.selected);\n }\n },\n\n /**\n * Select\n */\n select: function(option) {\n // ignore if it's already selected\n if (option === this.selected) {\n return;\n }\n\n if (this.dom.ul) {\n if (this.selected !== -1) {\n this.dom.options[this.selected].className = '';\n }\n\n this._addSelectedOptionClass(option);\n }\n\n this.selected = option;\n\n // set the button text to the selected option\n this.setBtnText($(this.options[option].label).text());\n\n this.base(option);\n },\n\n /**\n * Add Selected Option Class\n */\n _addSelectedOptionClass: function(option) {\n this.dom.options[option].className = 'sel';\n },\n\n /**\n * Set Button Text\n */\n setBtnText: function(text) {\n this.dom.$btnLabel.text(text);\n }\n\n },\n {\n defaults: {\n ulClass: 'menu select'\n }\n }\n);\n","import Garnish from './Garnish.js';\nimport Base from './Base.js';\nimport $ from 'jquery';\n\n/**\n * Keyboard shortcut manager class\n *\n * This can be used to map keyboard events to the current UI \"layer\" (whether that's the base document,\n * a modal, an HUD, or a menu).\n */\nexport default Base.extend(\n {\n shortcuts: null,\n layer: 0,\n\n init: function() {\n this.shortcuts = [[]];\n this.addListener(Garnish.$bod, 'keydown', 'triggerShortcut');\n },\n\n addLayer: function() {\n this.layer++;\n this.shortcuts.push([]);\n return this;\n },\n\n removeLayer: function() {\n if (this.layer === 0) {\n throw 'Can’t remove the base layer.';\n }\n this.layer--;\n this.shortcuts.pop();\n return this;\n },\n\n registerShortcut: function(shortcut, callback, layer) {\n shortcut = this._normalizeShortcut(shortcut);\n if (typeof layer === 'undefined') {\n layer = this.layer;\n }\n this.shortcuts[layer].push({\n key: JSON.stringify(shortcut),\n shortcut: shortcut,\n callback: callback,\n });\n return this;\n },\n\n unregisterShortcut: function(shortcut, layer) {\n shortcut = this._normalizeShortcut(shortcut);\n var key = JSON.stringify(shortcut);\n if (typeof layer === 'undefined') {\n layer = this.layer;\n }\n for (var i = 0; i < this.shortcuts[layer].length; i++) {\n if (this.shortcuts[layer][i].key === key) {\n this.shortcuts[layer].splice(i, 1);\n break;\n }\n }\n return this;\n },\n\n _normalizeShortcut: function(shortcut) {\n if (typeof shortcut === 'number') {\n shortcut = {keyCode: shortcut};\n }\n\n if (typeof shortcut.keyCode !== 'number') {\n throw 'Invalid shortcut';\n }\n\n return {\n keyCode: shortcut.keyCode,\n ctrl: !!shortcut.ctrl,\n shift: !!shortcut.shift,\n alt: !!shortcut.alt,\n };\n },\n\n triggerShortcut: function(ev) {\n var shortcut;\n for (var i = 0; i < this.shortcuts[this.layer].length; i++) {\n shortcut = this.shortcuts[this.layer][i].shortcut;\n if (\n shortcut.keyCode === ev.keyCode &&\n shortcut.ctrl === Garnish.isCtrlKeyPressed(ev) &&\n shortcut.shift === ev.shiftKey &&\n shortcut.alt === ev.altKey\n ) {\n ev.preventDefault();\n this.shortcuts[this.layer][i].callback(ev);\n break;\n }\n }\n },\n }\n);\n","import $ from 'jquery';\nimport Base from './Base.js';\nimport BaseDrag from './BaseDrag.js';\nimport CheckboxSelect from './CheckboxSelect.js';\nimport ContextMenu from './ContextMenu.js';\nimport CustomSelect from './CustomSelect.js';\nimport DisclosureMenu from './DisclosureMenu.js';\nimport Drag from './Drag.js';\nimport DragDrop from './DragDrop.js';\nimport DragMove from './DragMove.js';\nimport DragSort from './DragSort.js';\nimport EscManager from './EscManager.js';\nimport HUD from './HUD.js';\nimport MenuBtn from './MenuBtn.js';\nimport MixedInput from './MixedInput.js';\nimport Modal from './Modal.js';\nimport NiceText from './NiceText.js';\nimport Select from './Select.js';\nimport SelectMenu from './SelectMenu.js';\nimport ShortcutManager from './ShortcutManager.js';\n\n/**\n * @namespace Garnish\n */\n\n// Bail if Garnish is already defined\nif (typeof Garnish !== 'undefined') {\n throw 'Garnish is already defined!';\n}\n\nlet Garnish = {\n // jQuery objects for common elements\n $win: $(window),\n $doc: $(document),\n $bod: $(document.body)\n\n};\n\nGarnish.rtl = Garnish.$bod.hasClass('rtl');\nGarnish.ltr = !Garnish.rtl;\n\nGarnish = $.extend(Garnish, {\n\n $scrollContainer: Garnish.$win,\n\n // Key code constants\n DELETE_KEY: 8,\n SHIFT_KEY: 16,\n CTRL_KEY: 17,\n ALT_KEY: 18,\n RETURN_KEY: 13,\n ESC_KEY: 27,\n SPACE_KEY: 32,\n LEFT_KEY: 37,\n UP_KEY: 38,\n RIGHT_KEY: 39,\n DOWN_KEY: 40,\n A_KEY: 65,\n S_KEY: 83,\n CMD_KEY: 91,\n\n // Mouse button constants\n PRIMARY_CLICK: 1,\n SECONDARY_CLICK: 3,\n\n // Axis constants\n X_AXIS: 'x',\n Y_AXIS: 'y',\n\n FX_DURATION: 100,\n\n // Node types\n TEXT_NODE: 3,\n\n /**\n * Logs a message to the browser's console, if the browser has one.\n *\n * @param {string} msg\n */\n log: function(msg) {\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log(msg);\n }\n },\n\n _isMobileBrowser: null,\n _isMobileOrTabletBrowser: null,\n\n /**\n * Returns whether this is a mobile browser.\n * Detection script courtesy of http://detectmobilebrowsers.com\n *\n * Last updated: 2014-11-24\n *\n * @param {boolean} detectTablets\n * @return {boolean}\n */\n isMobileBrowser: function(detectTablets) {\n var key = detectTablets ? '_isMobileOrTabletBrowser' : '_isMobileBrowser';\n\n if (Garnish[key] === null) {\n var a = navigator.userAgent || navigator.vendor || window.opera;\n Garnish[key] = ((new RegExp('(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino' + (detectTablets ? '|android|ipad|playbook|silk' : ''), 'i')).test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\\-|your|zeto|zte\\-/i.test(a.substr(0, 4)));\n }\n\n return Garnish[key];\n },\n\n /**\n * Returns whether a variable is an array.\n *\n * @param {object} val\n * @return {boolean}\n */\n isArray: function(val) {\n return (val instanceof Array);\n },\n\n /**\n * Returns whether a variable is a jQuery collection.\n *\n * @param {object} val\n * @return {boolean}\n */\n isJquery: function(val) {\n return (val instanceof $);\n },\n\n /**\n * Returns whether a variable is a string.\n *\n * @param {object} val\n * @return {boolean}\n */\n isString: function(val) {\n return (typeof val === 'string');\n },\n\n /**\n * Returns whether an element has an attribute.\n *\n * @see http://stackoverflow.com/questions/1318076/jquery-hasattr-checking-to-see-if-there-is-an-attribute-on-an-element/1318091#1318091\n */\n hasAttr: function(elem, attr) {\n var val = $(elem).attr(attr);\n return (typeof val !== 'undefined' && val !== false);\n },\n\n /**\n * Returns whether something is a text node.\n *\n * @param {object} elem\n * @return {boolean}\n */\n isTextNode: function(elem) {\n return (elem.nodeType === Garnish.TEXT_NODE);\n },\n\n /**\n * Returns the offset of an element within the scroll container, whether that's the window or something else\n */\n getOffset: function(elem) {\n this.getOffset._offset = $(elem).offset();\n\n if (Garnish.$scrollContainer[0] !== Garnish.$win[0]) {\n this.getOffset._offset.top += Garnish.$scrollContainer.scrollTop();\n this.getOffset._offset.left += Garnish.$scrollContainer.scrollLeft();\n }\n\n return this.getOffset._offset;\n },\n\n /**\n * Returns the distance between two coordinates.\n *\n * @param {number} x1 The first coordinate's X position.\n * @param {number} y1 The first coordinate's Y position.\n * @param {number} x2 The second coordinate's X position.\n * @param {number} y2 The second coordinate's Y position.\n * @return {number}\n */\n getDist: function(x1, y1, x2, y2) {\n return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));\n },\n\n /**\n * Returns whether an element is touching an x/y coordinate.\n *\n * @param {number} x The coordinate's X position.\n * @param {number} y The coordinate's Y position.\n * @param {object} elem Either an actual element or a jQuery collection.\n * @return {boolean}\n */\n hitTest: function(x, y, elem) {\n Garnish.hitTest._$elem = $(elem);\n Garnish.hitTest._offset = Garnish.hitTest._$elem.offset();\n Garnish.hitTest._x1 = Garnish.hitTest._offset.left;\n Garnish.hitTest._y1 = Garnish.hitTest._offset.top;\n Garnish.hitTest._x2 = Garnish.hitTest._x1 + Garnish.hitTest._$elem.outerWidth();\n Garnish.hitTest._y2 = Garnish.hitTest._y1 + Garnish.hitTest._$elem.outerHeight();\n\n return (x >= Garnish.hitTest._x1 && x < Garnish.hitTest._x2 && y >= Garnish.hitTest._y1 && y < Garnish.hitTest._y2);\n },\n\n /**\n * Returns whether the cursor is touching an element.\n *\n * @param {object} ev The mouse event object containing pageX and pageY properties.\n * @param {object} elem Either an actual element or a jQuery collection.\n * @return {boolean}\n */\n isCursorOver: function(ev, elem) {\n return Garnish.hitTest(ev.pageX, ev.pageY, elem);\n },\n\n /**\n * Copies text styles from one element to another.\n *\n * @param {object} source The source element. Can be either an actual element or a jQuery collection.\n * @param {object} target The target element. Can be either an actual element or a jQuery collection.\n */\n copyTextStyles: function(source, target) {\n var $source = $(source),\n $target = $(target);\n\n $target.css({\n fontFamily: $source.css('fontFamily'),\n fontSize: $source.css('fontSize'),\n fontWeight: $source.css('fontWeight'),\n letterSpacing: $source.css('letterSpacing'),\n lineHeight: $source.css('lineHeight'),\n textAlign: $source.css('textAlign'),\n textIndent: $source.css('textIndent'),\n whiteSpace: $source.css('whiteSpace'),\n wordSpacing: $source.css('wordSpacing'),\n wordWrap: $source.css('wordWrap')\n });\n },\n\n /**\n * Returns the body's real scrollTop, discarding any window banding in Safari.\n *\n * @return {number}\n */\n getBodyScrollTop: function() {\n Garnish.getBodyScrollTop._scrollTop = document.body.scrollTop;\n\n if (Garnish.getBodyScrollTop._scrollTop < 0) {\n Garnish.getBodyScrollTop._scrollTop = 0;\n }\n else {\n Garnish.getBodyScrollTop._maxScrollTop = Garnish.$bod.outerHeight() - Garnish.$win.height();\n\n if (Garnish.getBodyScrollTop._scrollTop > Garnish.getBodyScrollTop._maxScrollTop) {\n Garnish.getBodyScrollTop._scrollTop = Garnish.getBodyScrollTop._maxScrollTop;\n }\n }\n\n return Garnish.getBodyScrollTop._scrollTop;\n },\n\n requestAnimationFrame: (function() {\n var raf = (\n window.requestAnimationFrame ||\n window.mozRequestAnimationFrame ||\n window.webkitRequestAnimationFrame ||\n function(fn) {\n return window.setTimeout(fn, 20);\n }\n );\n\n return function(fn) {\n return raf(fn);\n };\n })(),\n\n cancelAnimationFrame: (function() {\n var cancel = (\n window.cancelAnimationFrame ||\n window.mozCancelAnimationFrame ||\n window.webkitCancelAnimationFrame ||\n window.clearTimeout\n );\n\n return function(id) {\n return cancel(id);\n };\n })(),\n\n /**\n * Scrolls a container element to an element within it.\n *\n * @param {object} container Either an actual element or a jQuery collection.\n * @param {object} elem Either an actual element or a jQuery collection.\n */\n scrollContainerToElement: function(container, elem) {\n var $elem;\n\n if (typeof elem === 'undefined') {\n $elem = $(container);\n $container = $elem.scrollParent();\n }\n else {\n var $container = $(container);\n $elem = $(elem);\n }\n\n if ($container.prop('nodeName') === 'HTML' || $container[0] === Garnish.$doc[0]) {\n $container = Garnish.$win;\n }\n\n var scrollTop = $container.scrollTop(),\n elemOffset = $elem.offset().top;\n\n var elemScrollOffset;\n\n if ($container[0] === window) {\n elemScrollOffset = elemOffset - scrollTop;\n }\n else {\n elemScrollOffset = elemOffset - $container.offset().top;\n }\n\n var targetScrollTop = false;\n\n // Is the element above the fold?\n if (elemScrollOffset < 0) {\n targetScrollTop = scrollTop + elemScrollOffset - 10;\n }\n else {\n var elemHeight = $elem.outerHeight(),\n containerHeight = ($container[0] === window ? window.innerHeight : $container[0].clientHeight);\n\n // Is it below the fold?\n if (elemScrollOffset + elemHeight > containerHeight) {\n targetScrollTop = scrollTop + (elemScrollOffset - (containerHeight - elemHeight)) + 10;\n }\n }\n\n if (targetScrollTop !== false) {\n // Velocity only allows you to scroll to an arbitrary position if you're scrolling the main window\n if ($container[0] === window) {\n $('html').velocity('scroll', {\n offset: targetScrollTop + 'px',\n mobileHA: false\n });\n }\n else {\n $container.scrollTop(targetScrollTop);\n }\n }\n },\n\n SHAKE_STEPS: 10,\n SHAKE_STEP_DURATION: 25,\n\n /**\n * Shakes an element.\n *\n * @param {object} elem Either an actual element or a jQuery collection.\n * @param {string} prop The property that should be adjusted (default is 'margin-left').\n */\n shake: function(elem, prop) {\n var $elem = $(elem);\n\n if (!prop) {\n prop = 'margin-left';\n }\n\n var startingPoint = parseInt($elem.css(prop));\n if (isNaN(startingPoint)) {\n startingPoint = 0;\n }\n\n for (var i = 0; i <= Garnish.SHAKE_STEPS; i++) {\n (function(i) {\n setTimeout(function() {\n Garnish.shake._properties = {};\n Garnish.shake._properties[prop] = startingPoint + (i % 2 ? -1 : 1) * (10 - i);\n $elem.velocity(Garnish.shake._properties, Garnish.SHAKE_STEP_DURATION);\n }, (Garnish.SHAKE_STEP_DURATION * i));\n })(i);\n }\n },\n\n /**\n * Returns the first element in an array or jQuery collection.\n *\n * @param {object} elem\n * @return mixed\n */\n getElement: function(elem) {\n return $.makeArray(elem)[0];\n },\n\n /**\n * Returns the beginning of an input's name= attribute value with any [bracktes] stripped out.\n *\n * @param {object} elem\n * @return string|null\n */\n getInputBasename: function(elem) {\n var name = $(elem).attr('name');\n\n if (name) {\n return name.replace(/\\[.*/, '');\n }\n else {\n return null;\n }\n },\n\n /**\n * Returns an input's value as it would be POSTed.\n * So unchecked checkboxes and radio buttons return null,\n * and multi-selects whose name don't end in \"[]\" only return the last selection\n *\n * @param {object} $input\n * @return {(string|string[])}\n */\n getInputPostVal: function($input) {\n var type = $input.attr('type'),\n val = $input.val();\n\n // Is this an unchecked checkbox or radio button?\n if ((type === 'checkbox' || type === 'radio')) {\n if ($input.prop('checked')) {\n return val;\n }\n else {\n return null;\n }\n }\n\n // Flatten any array values whose input name doesn't end in \"[]\"\n // - e.g. a multi-select\n else if (Garnish.isArray(val) && $input.attr('name').substr(-2) !== '[]') {\n if (val.length) {\n return val[val.length - 1];\n }\n else {\n return null;\n }\n }\n\n // Just return the value\n else {\n return val;\n }\n },\n\n /**\n * Returns the inputs within a container\n *\n * @param {object} container The container element. Can be either an actual element or a jQuery collection.\n * @return {object}\n */\n findInputs: function(container) {\n return $(container).find('input,text,textarea,select,button');\n },\n\n /**\n * Returns the post data within a container.\n *\n * @param {object} container\n * @return {array}\n */\n getPostData: function(container) {\n var postData = {},\n arrayInputCounters = {},\n $inputs = Garnish.findInputs(container);\n\n var inputName;\n\n for (var i = 0; i < $inputs.length; i++) {\n var $input = $inputs.eq(i);\n\n if ($input.prop('disabled')) {\n continue;\n }\n\n inputName = $input.attr('name');\n if (!inputName) {\n continue;\n }\n\n var inputVal = Garnish.getInputPostVal($input);\n if (inputVal === null) {\n continue;\n }\n\n var isArrayInput = (inputName.substr(-2) === '[]');\n\n if (isArrayInput) {\n // Get the cropped input name\n var croppedName = inputName.substring(0, inputName.length - 2);\n\n // Prep the input counter\n if (typeof arrayInputCounters[croppedName] === 'undefined') {\n arrayInputCounters[croppedName] = 0;\n }\n }\n\n if (!Garnish.isArray(inputVal)) {\n inputVal = [inputVal];\n }\n\n for (var j = 0; j < inputVal.length; j++) {\n if (isArrayInput) {\n inputName = croppedName + '[' + arrayInputCounters[croppedName] + ']';\n arrayInputCounters[croppedName]++;\n }\n\n postData[inputName] = inputVal[j];\n }\n }\n\n return postData;\n },\n\n copyInputValues: function(source, target) {\n var $sourceInputs = Garnish.findInputs(source),\n $targetInputs = Garnish.findInputs(target);\n\n for (var i = 0; i < $sourceInputs.length; i++) {\n if (typeof $targetInputs[i] === 'undefined') {\n break;\n }\n\n $targetInputs.eq(i).val(\n $sourceInputs.eq(i).val()\n );\n }\n },\n\n /**\n * Returns whether the \"Ctrl\" key is pressed (or ⌘ if this is a Mac) for a given keyboard event\n *\n * @param ev The keyboard event\n *\n * @return {boolean} Whether the \"Ctrl\" key is pressed\n */\n isCtrlKeyPressed: function(ev) {\n if (window.navigator.platform.match(/Mac/)) {\n // metaKey maps to ⌘ on Macs\n return ev.metaKey;\n }\n return ev.ctrlKey;\n },\n\n _eventHandlers: [],\n\n _normalizeEvents: function(events) {\n if (typeof events === 'string') {\n events = events.split(' ');\n }\n\n for (var i = 0; i < events.length; i++) {\n if (typeof events[i] === 'string') {\n events[i] = events[i].split('.');\n }\n }\n\n return events;\n },\n\n on: function(target, events, data, handler) {\n if (typeof data === 'function') {\n handler = data;\n data = {};\n }\n\n events = this._normalizeEvents(events);\n\n for (var i = 0; i < events.length; i++) {\n var ev = events[i];\n this._eventHandlers.push({\n target: target,\n type: ev[0],\n namespace: ev[1],\n data: data,\n handler: handler\n });\n }\n },\n\n off: function(target, events, handler) {\n events = this._normalizeEvents(events);\n\n for (var i = 0; i < events.length; i++) {\n var ev = events[i];\n\n for (var j = this._eventHandlers.length - 1; j >= 0; j--) {\n var eventHandler = this._eventHandlers[j];\n\n if (\n eventHandler.target === target &&\n eventHandler.type === ev[0] &&\n (!ev[1] || eventHandler.namespace === ev[1]) &&\n eventHandler.handler === handler\n ) {\n this._eventHandlers.splice(j, 1);\n }\n }\n }\n }\n});\n\nObject.assign(Garnish, {\n Base,\n BaseDrag,\n CheckboxSelect,\n ContextMenu,\n CustomSelect,\n DisclosureMenu,\n Drag,\n DragDrop,\n DragMove,\n DragSort,\n EscManager,\n HUD,\n MenuBtn,\n MixedInput,\n Modal,\n NiceText,\n Select,\n SelectMenu,\n ShortcutManager,\n});\n\n// Custom events\n// -----------------------------------------------------------------------------\n\nvar erd;\n\nfunction getErd() {\n if (typeof erd === 'undefined') {\n erd = elementResizeDetectorMaker({\n callOnAdd: false\n });\n }\n\n return erd;\n}\n\nfunction triggerResizeEvent(elem) {\n $(elem).trigger('resize');\n}\n\n// Work them into jQuery's event system\n$.extend($.event.special, {\n activate: {\n setup: function(data, namespaces, eventHandle) {\n var activateNamespace = this._namespace + '-activate';\n var $elem = $(this);\n\n $elem.on({\n 'mousedown.garnish-activate': function(e) {\n // Prevent buttons from getting focus on click\n e.preventDefault();\n },\n 'click.garnish-activate': function(e) {\n e.preventDefault();\n\n if (!$elem.hasClass('disabled')) {\n $elem.trigger('activate');\n }\n },\n 'keydown.garnish-activate': function(e) {\n // Ignore if the event was bubbled up, or if it wasn't the space key\n if (this !== $elem[0] || e.keyCode !== Garnish.SPACE_KEY) {\n return;\n }\n\n e.preventDefault();\n\n if (!$elem.hasClass('disabled')) {\n $elem.addClass('active');\n\n Garnish.$doc.on('keyup.garnish-activate', function(e) {\n $elem.removeClass('active');\n\n if (e.keyCode === Garnish.SPACE_KEY) {\n e.preventDefault();\n $elem.trigger('activate');\n }\n\n Garnish.$doc.off('keyup.garnish-activate');\n });\n }\n }\n });\n\n if (!$elem.hasClass('disabled')) {\n $elem.attr('tabindex', '0');\n } else {\n $elem.removeAttr('tabindex');\n }\n },\n teardown: function() {\n $(this).off('.garnish-activate');\n }\n },\n\n textchange: {\n setup: function(data, namespaces, eventHandle) {\n var $elem = $(this);\n $elem.data('garnish-textchange-value', $elem.val());\n $elem.on('keypress.garnish-textchange keyup.garnish-textchange change.garnish-textchange blur.garnish-textchange', function(e) {\n var val = $elem.val();\n if (val !== $elem.data('garnish-textchange-value')) {\n $elem.data('garnish-textchange-value', val);\n $elem.trigger('textchange');\n }\n });\n },\n teardown: function() {\n $(this).off('.garnish-textchange');\n },\n handle: function(ev, data) {\n var el = this;\n var args = arguments;\n var delay = data && typeof data.delay !== 'undefined' ? data.delay : (ev.data && ev.data.delay !== undefined ? ev.data.delay : null);\n var handleObj = ev.handleObj;\n var targetData = $.data(ev.target);\n\n // Was this event configured with a delay?\n if (delay) {\n if (targetData.delayTimeout) {\n clearTimeout(targetData.delayTimeout);\n }\n\n targetData.delayTimeout = setTimeout(function() {\n handleObj.handler.apply(el, args);\n }, delay);\n } else {\n return handleObj.handler.apply(el, args);\n }\n }\n },\n\n resize: {\n setup: function(data, namespaces, eventHandle) {\n // window is the only element that natively supports a resize event\n if (this === window) {\n return false;\n }\n\n $('> :last-child', this).addClass('last');\n getErd().listenTo(this, triggerResizeEvent)\n },\n teardown: function() {\n if (this === window) {\n return false;\n }\n\n getErd().removeListener(this, triggerResizeEvent);\n }\n }\n});\n\n// Give them their own element collection chaining methods\n$.each(['activate', 'textchange', 'resize'], function(i, name) {\n $.fn[name] = function(data, fn) {\n return arguments.length > 0 ?\n this.on(name, null, data, fn) :\n this.trigger(name);\n };\n});\n\nexport default Garnish;\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(55);\n"],"names":["___EXPOSE_LOADER_IMPORT___","require","___EXPOSE_LOADER_GLOBAL_THIS___","___EXPOSE_LOADER_IMPORT_MODULE_LOCAL_NAME___","module","exports","globalThis","g","this","Function","e","window","self","jQuery","Base","extend","_instance","_static","prototype","_prototyping","proto","call","base","constructor","klass","_constructing","apply","arguments","ancestor","forEach","implement","toString","valueOf","type","init","source","value","length","test","method","previous","returnValue","toSource","hidden","i","key","desc","Object","getOwnPropertyDescriptor","defineProperty","version","object","block","context","undefined","String","settings","_eventHandlers","_namespace","_$listeners","_disabled","Math","floor","random","_listeners","$","setSettings","defaults","baseSettings","on","events","data","handler","Garnish","ev","push","namespace","off","j","eventHandler","splice","trigger","_ev","target","_splitEvents","split","_formatEvents","slice","join","addListener","elem","func","$elem","bind","removeListener","removeAllListeners","disable","enable","destroy","$items","dragging","mousedownX","mousedownY","realMouseX","realMouseY","mouseX","mouseY","mouseDistX","mouseDistY","mouseOffsetX","mouseOffsetY","$targetItem","scrollProperty","scrollAxis","scrollDist","scrollProxy","scrollFrame","_","items","addItems","allowDragging","startDragging","onDragStart","drag","didMouseMove","_scrollProperty","axis","_winScrollTop","_minMouseScrollY","_scrollAxis","_scrollDist","round","_maxMouseScrollY","_winScrollLeft","_minMouseScrollX","_maxMouseScrollX","_scrollWindow","_cancelWindowScroll","onDrag","stopDragging","onDragStop","item","removeItems","add","index","_deinitItem","removeAllItems","_handleMouseDown","which","$target","$handle","_getItemHandle","currentTarget","is","closest","ignoreHandleSelector","preventDefault","pageX","pageY","offset","left","top","handle","_handleMouseMove","_mouseDist","_handleMouseUp","scrollPos","minMouseDist","windowScrollTargetSize","$container","$all","$options","container","$checkboxes","find","filter","not","onAllChange","isAllChecked","prop","checked","disabled","removeData","options","$menu","showingMenu","buildMenu","menuClass","$ul","appendTo","hasOwnProperty","option","$li","$a","label","onClick","setTimeout","mousedown","hideMenu","showMenu","showing","document","body","show","css","hide","counter","visible","$anchor","menuId","_windowWidth","_windowHeight","_windowScrollLeft","_windowScrollTop","_anchorOffset","_anchorWidth","_anchorHeight","_anchorOffsetRight","_anchorOffsetBottom","_menuWidth","_menuHeight","addOptions","$menuList","attr","role","id","attachToElement","anchor","stopPropagation","nodeName","each","optionKey","tabindex","selectOption","setPositionRelativeToAnchor","outerWidth","outerHeight","width","topClearance","bottomClearance","maxHeight","windowSpacing","min","align","_alignCenter","rightClearance","leftClearance","_alignRight","_alignLeft","velocity","opacity","display","registerShortcut","duration","detach","onOptionSelect","selectedOption","right","$trigger","$alignmentElement","$wrapper","_wrapperElementOffset","_alignmentElementOffset","_triggerWidth","_triggerHeight","triggerId","alignmentSelector","wrapper","addDisclosureMenuEventListeners","handleTriggerClick","event","handleKeypress","handleMousedown","focusElement","direction","newIndex","currentFocus","focusable","currentIndex","focus","newTarget","triggerButton","newTargetIsInsideDisclosure","has","keyCode","isExpanded","setContainerPosition","firstFocusableEl","getBoundingClientRect","bottom","topAdjustment","bottomAdjustment","leftAdjustment","rightAdjustment","BaseDrag","targetItemWidth","targetItemHeight","targetItemPositionInDraggee","$draggee","otherItems","totalOtherItems","helpers","helperTargets","helperPositions","helperLagIncrement","updateHelperPosProxy","updateHelperPosFrame","lastMouseX","lastMouseY","_returningHelpersToDraggees","draggeeDisplay","setDraggee","findDraggee","_updateHelperPos","helperLagIncrementDividend","concat","toArray","singleHelper","_createHelper","removeDraggee","collapseDraggees","appendDraggee","$newDraggee","oldLength","newLength","draggeeVirtualMidpointX","draggeeVirtualMidpointY","getHelperTargetX","getHelperTargetY","returnHelpersToDraggees","eq","$helper","visibility","callback","draggeeOffset","_showDraggee","onReturnHelpersToDraggees","$draggeeHelper","clone","addClass","copyDraggeeInputValuesToHelper","ceil","margin","helper","append","helperPos","_getHelperTarget","position","zIndex","helperBaseZindex","helperOpacity","_i","_j","_lag","helperLagBase","helperSpacingX","helperSpacingY","remove","Drag","$dropTargets","$activeDropTarget","updateDropTargets","dropTargets","_activeDropTarget","_elem","removeClass","activeDropTargetClass","onDropTargetChange","fadeOutHelpers","complete","$heightedContainer","$insertion","insertionVisible","oldDraggeeIndexes","newDraggeeIndexes","closestItem","_midpointVersion","_$prevItem","createInsertion","insertion","magnetStrength","_draggeeOffsetX","_draggeeOffsetY","canInsertBefore","$item","canInsertAfter","_getDraggeeIndexes","moveTargetItemToFront","_getItemIndex","first","insertBefore","_placeInsertionWithDraggee","_clearMidpoints","height","parent","_removeInsertion","_getClosestItem","_updateInsertion","insertAfter","onSortChange","onInsertionPointChange","indexes","_closestItem","_testForClosestItem","_midpoint","_getItemMidpoint","_startXDist","_lastXDist","abs","x","_startYDist","_lastYDist","y","_$otherItem","prev","_xDist","_yDist","last","next","_repositionDraggee","get","_moveDraggeeToItem","_$item","_offset","prependTo","_mouseDistX","_mouseDistY","_closestItemMouseDistY","_closestItemMouseDistX","handlers","escapeLatest","register","obj","unregister","pop","$fixedTriggerParent","$hud","$tip","$body","$header","$footer","$mainContainer","$main","$shade","orientation","updatingSizeAndPosition","windowWidth","windowHeight","scrollTop","scrollLeft","mainWidth","mainHeight","bodyContents","onShow","onHide","onSubmit","shadeClass","hudClass","tipClass","bodyClass","mainContainerClass","mainClass","updateBody","$parent","offsetParent","hideOnShadeClick","closeBtn","html","headerClass","footerClass","closeOtherHUDs","hudID","hideOnEsc","updateRecords","updateSizeAndPosition","changed","force","updateSizeAndPositionInternal","triggerWidth","triggerHeight","triggerOffset","windowScrollLeft","windowScrollTop","scrollContainerTriggerOffset","scrollContainerScrollLeft","scrollContainerScrollTop","hudBodyWidth","hudBodyHeight","maxHudBodyWidth","maxHudBodyHeight","triggerCenter","clearances","orientations","relevantSize","triggerSpacing","minBodyWidth","minBodyHeight","maxLeft","minLeft","tipLeft","tipWidth","maxTop","minTop","tipTop","toggle","submit","_handleSubmit","tipClasses","$btn","menu","btn","menuAnchor","onMenuHide","onBlur","activeElement","onKeyDown","$option","$currentOption","click","focusOption","hasClass","onMouseDown","elements","focussedElement","blurTimeout","getElementIndex","isText","onFocus","setFocus","setCarotPos","addTextElement","text","TextElement","addElement","$input","focussedElementIndex","selectionStart","selectionEnd","val","preVal","substring","postVal","substr","newText","removeElement","$prevElem","$nextElem","prevElemVal","newVal","blurFocussedElement","focusPreviousElement","$from","focusNextElement","pos","parentInput","$stage","focussed","interval","padding","setWidth","getIndex","buildStage","wordWrap","getTextWidth","replace","stageWidth","setInterval","checkInput","clearInterval","getVal","setVal","onChange","dragger","desiredWidth","desiredHeight","resizeDragger","resizeStartWidth","resizeStartHeight","setContainer","autoShow","draggable","dragHandleSelector","resizable","$resizeDragHandle","_handleResizeStart","_handleResize","closeOtherModals","onFadeIn","quickShow","onFadeOut","quickHide","max","_width","getWidth","minGutter","_height","getHeight","_handleWindowResize","relativeElemPadding","instances","visibleModal","$hint","$charsLeft","autoHeight","maxLength","showCharsLeft","showingHint","inputBoxSizing","minHeight","initialized","input","isVisible","initialize","parseInt","removeAttr","getHeightForValue","updateHeight","hint","$hintContainer","charsLeftClass","updateCharsLeft","initializeIfVisible","showHint","hideHint","onTextChange","spaces","onHeightChange","updateHeightIfWidthChanged","_charsLeft","negativeCharsLeftClass","hintFadeDuration","charsLeftHtml","charsLeft","$selectedItems","$focusedItem","mousedownTarget","mouseUpTimeout","callbackFrame","$focusable","$first","$last","allowEmpty","checkboxMode","ignoreClick","deselectAll","getItemIndex","isSelected","selectItem","preventScroll","multi","setFocusableItem","focusItem","_selectItems","selectAll","selectRange","sliceFrom","sliceTo","deselectItem","_deselectItems","clearFirst","deselectOthers","toggleItem","_canDeselect","clearMouseUpTimeout","clearTimeout","getFirstItem","getLastItem","isPreviousItem","isNextItem","getPreviousItem","getNextItem","getItemToTheLeft","horizontal","vertical","getClosestItem","getItemToTheRight","getItemAbove","getItemBelow","dir","step","axisProps","dirProps","$thisItem","thisOffset","thisMidpoint","midpointOffset","midpointSizeFunc","otherRowPos","smallestMidpointDiff","$closestItem","$otherItem","otherOffset","isNextRow","rowOffset","otherMidpoint","midpointDiff","isWrongDirection","getFurthestItemToTheLeft","getFurthestItem","getFurthestItemToTheRight","getFurthestItemAbove","getFurthestItemBelow","$testItem","totalSelected","getTotalSelected","updateIndexes","itemsChanged","selectionChanged","selectedIndex","selectedClass","onSelectionChange","resetItemOrder","getSelectedItems","shiftKey","_actAsCheckbox","onMouseUp","ctrlKey","closestItemAxisProps","closestItemDirectionProps","a","b","CustomSelect","selected","build","_addSelectedOptionClass","select","dom","ul","className","setBtnText","$btnLabel","ulClass","shortcuts","layer","addLayer","removeLayer","shortcut","_normalizeShortcut","JSON","stringify","unregisterShortcut","ctrl","shift","alt","triggerShortcut","altKey","cancel","raf","erd","$win","$doc","$bod","getErd","elementResizeDetectorMaker","callOnAdd","triggerResizeEvent","rtl","ltr","$scrollContainer","DELETE_KEY","SHIFT_KEY","CTRL_KEY","ALT_KEY","RETURN_KEY","ESC_KEY","SPACE_KEY","LEFT_KEY","UP_KEY","RIGHT_KEY","DOWN_KEY","A_KEY","S_KEY","CMD_KEY","PRIMARY_CLICK","SECONDARY_CLICK","X_AXIS","Y_AXIS","FX_DURATION","TEXT_NODE","log","msg","console","_isMobileBrowser","_isMobileOrTabletBrowser","isMobileBrowser","detectTablets","navigator","userAgent","vendor","opera","RegExp","isArray","Array","isJquery","isString","hasAttr","isTextNode","nodeType","getOffset","getDist","x1","y1","x2","y2","sqrt","pow","hitTest","_$elem","_x1","_y1","_x2","_y2","isCursorOver","copyTextStyles","$source","fontFamily","fontSize","fontWeight","letterSpacing","lineHeight","textAlign","textIndent","whiteSpace","wordSpacing","getBodyScrollTop","_scrollTop","_maxScrollTop","requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","fn","cancelAnimationFrame","mozCancelAnimationFrame","webkitCancelAnimationFrame","scrollContainerToElement","scrollParent","elemScrollOffset","elemOffset","targetScrollTop","elemHeight","containerHeight","innerHeight","clientHeight","mobileHA","SHAKE_STEPS","SHAKE_STEP_DURATION","shake","startingPoint","isNaN","_properties","getElement","getInputBasename","name","getInputPostVal","findInputs","getPostData","inputName","postData","arrayInputCounters","$inputs","inputVal","isArrayInput","croppedName","copyInputValues","$sourceInputs","$targetInputs","isCtrlKeyPressed","platform","match","metaKey","_normalizeEvents","assign","CheckboxSelect","ContextMenu","DisclosureMenu","DragDrop","DragMove","DragSort","EscManager","HUD","MenuBtn","MixedInput","Modal","NiceText","Select","SelectMenu","ShortcutManager","activate","setup","namespaces","eventHandle","teardown","textchange","el","args","delay","handleObj","targetData","delayTimeout","resize","listenTo","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","__esModule","d","definition","o","enumerable","r","Symbol","toStringTag"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/web/assets/garnish/src/Base.js b/src/web/assets/garnish/src/Base.js
new file mode 100644
index 00000000000..aaf4ae359c6
--- /dev/null
+++ b/src/web/assets/garnish/src/Base.js
@@ -0,0 +1,176 @@
+import Base from './lib/Base.js';
+import Garnish from './Garnish.js';
+import $ from 'jquery';
+
+/**
+ * Garnish base class
+ */
+ export default Base.extend({
+
+ settings: null,
+
+ _eventHandlers: null,
+ _namespace: null,
+ _$listeners: null,
+ _disabled: false,
+
+ constructor: function() {
+ this._eventHandlers = [];
+ this._namespace = '.Garnish' + Math.floor(Math.random() * 1000000000);
+ this._listeners = [];
+ this.init.apply(this, arguments);
+ },
+
+ init: $.noop,
+
+ setSettings: function(settings, defaults) {
+ var baseSettings = (typeof this.settings === 'undefined' ? {} : this.settings);
+ this.settings = $.extend({}, baseSettings, defaults, settings);
+ },
+
+ on: function(events, data, handler) {
+ if (typeof data === 'function') {
+ handler = data;
+ data = {};
+ }
+
+ events = Garnish._normalizeEvents(events);
+
+ for (var i = 0; i < events.length; i++) {
+ var ev = events[i];
+ this._eventHandlers.push({
+ type: ev[0],
+ namespace: ev[1],
+ data: data,
+ handler: handler
+ });
+ }
+ },
+
+ off: function(events, handler) {
+ events = Garnish._normalizeEvents(events);
+
+ for (var i = 0; i < events.length; i++) {
+ var ev = events[i];
+
+ for (var j = this._eventHandlers.length - 1; j >= 0; j--) {
+ var eventHandler = this._eventHandlers[j];
+
+ if (
+ eventHandler.type === ev[0] &&
+ (!ev[1] || eventHandler.namespace === ev[1]) &&
+ eventHandler.handler === handler
+ ) {
+ this._eventHandlers.splice(j, 1);
+ }
+ }
+ }
+ },
+
+ trigger: function(type, data) {
+ var ev = {
+ type: type,
+ target: this
+ };
+
+ // instance level event handlers
+ var i, handler, _ev;
+ for (i = 0; i < this._eventHandlers.length; i++) {
+ handler = this._eventHandlers[i];
+
+ if (handler.type === type) {
+ _ev = $.extend({data: handler.data}, data, ev);
+ handler.handler(_ev);
+ }
+ }
+
+ // class level event handlers
+ for (i = 0; i < Garnish._eventHandlers.length; i++) {
+ handler = Garnish._eventHandlers[i];
+
+ if (this instanceof handler.target && handler.type === type) {
+ _ev = $.extend({data: handler.data}, data, ev);
+ handler.handler(_ev);
+ }
+ }
+ },
+
+ _splitEvents: function(events) {
+ if (typeof events === 'string') {
+ events = events.split(',');
+
+ for (var i = 0; i < events.length; i++) {
+ events[i] = $.trim(events[i]);
+ }
+ }
+
+ return events;
+ },
+
+ _formatEvents: function(events) {
+ events = this._splitEvents(events).slice(0);
+
+ for (var i = 0; i < events.length; i++) {
+ events[i] += this._namespace;
+ }
+
+ return events.join(' ');
+ },
+
+ addListener: function(elem, events, data, func) {
+ var $elem = $(elem);
+
+ // Ignore if there aren't any elements
+ if (!$elem.length) {
+ return;
+ }
+
+ events = this._splitEvents(events);
+
+ // Param mapping
+ if (typeof func === 'undefined' && typeof data !== 'object') {
+ // (elem, events, func)
+ func = data;
+ data = {};
+ }
+
+ if (typeof func === 'function') {
+ func = func.bind(this);
+ }
+ else {
+ func = this[func].bind(this);
+ }
+
+ $elem.on(this._formatEvents(events), data, $.proxy(function() {
+ if (!this._disabled) {
+ return func.apply(this, arguments);
+ }
+ }, this));
+
+ // Remember that we're listening to this element
+ if ($.inArray(elem, this._listeners) === -1) {
+ this._listeners.push(elem);
+ }
+ },
+
+ removeListener: function(elem, events) {
+ $(elem).off(this._formatEvents(events));
+ },
+
+ removeAllListeners: function(elem) {
+ $(elem).off(this._namespace);
+ },
+
+ disable: function() {
+ this._disabled = true;
+ },
+
+ enable: function() {
+ this._disabled = false;
+ },
+
+ destroy: function() {
+ this.trigger('destroy');
+ this.removeAllListeners(this._listeners);
+ }
+});
diff --git a/packages/craftcms-garnish/src/BaseDrag.js b/src/web/assets/garnish/src/BaseDrag.js
similarity index 99%
rename from packages/craftcms-garnish/src/BaseDrag.js
rename to src/web/assets/garnish/src/BaseDrag.js
index 3c6b8d4d901..2ad440d849a 100644
--- a/packages/craftcms-garnish/src/BaseDrag.js
+++ b/src/web/assets/garnish/src/BaseDrag.js
@@ -1,11 +1,15 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Base drag class
*
* Does all the grunt work for manipulating elements via click-and-drag,
* while leaving the actual element manipulation up to a subclass.
*/
-Garnish.BaseDrag = Garnish.Base.extend(
+
+export default Base.extend(
{
$items: null,
diff --git a/packages/craftcms-garnish/src/CheckboxSelect.js b/src/web/assets/garnish/src/CheckboxSelect.js
similarity index 90%
rename from packages/craftcms-garnish/src/CheckboxSelect.js
rename to src/web/assets/garnish/src/CheckboxSelect.js
index 72e248d7a2c..fb4a275d893 100644
--- a/packages/craftcms-garnish/src/CheckboxSelect.js
+++ b/src/web/assets/garnish/src/CheckboxSelect.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Checkbox select class
*/
-Garnish.CheckboxSelect = Garnish.Base.extend(
+export default Base.extend(
{
$container: null,
$all: null,
diff --git a/packages/craftcms-garnish/src/ContextMenu.js b/src/web/assets/garnish/src/ContextMenu.js
similarity index 97%
rename from packages/craftcms-garnish/src/ContextMenu.js
rename to src/web/assets/garnish/src/ContextMenu.js
index d51d0c44105..aaab7d93c67 100644
--- a/packages/craftcms-garnish/src/ContextMenu.js
+++ b/src/web/assets/garnish/src/ContextMenu.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Context Menu
*/
-Garnish.ContextMenu = Garnish.Base.extend(
+export default Base.extend(
{
$target: null,
options: null,
diff --git a/packages/craftcms-garnish/src/CustomSelect.js b/src/web/assets/garnish/src/CustomSelect.js
similarity index 98%
rename from packages/craftcms-garnish/src/CustomSelect.js
rename to src/web/assets/garnish/src/CustomSelect.js
index e6802478b7f..842ad4c699d 100644
--- a/packages/craftcms-garnish/src/CustomSelect.js
+++ b/src/web/assets/garnish/src/CustomSelect.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Custom Select Menu
*/
-Garnish.CustomSelect = Garnish.Base.extend(
+export default Base.extend(
{
settings: null,
visible: false,
@@ -270,8 +273,3 @@ Garnish.CustomSelect = Garnish.Base.extend(
},
}
);
-
-/**
- * @deprecated
- */
-Garnish.Menu = Garnish.CustomSelect;
diff --git a/packages/craftcms-garnish/src/DisclosureMenu.js b/src/web/assets/garnish/src/DisclosureMenu.js
similarity index 98%
rename from packages/craftcms-garnish/src/DisclosureMenu.js
rename to src/web/assets/garnish/src/DisclosureMenu.js
index c2c448a9d9c..cd4969631f8 100644
--- a/packages/craftcms-garnish/src/DisclosureMenu.js
+++ b/src/web/assets/garnish/src/DisclosureMenu.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Disclosure Widget
*/
-Garnish.DisclosureMenu = Garnish.Base.extend(
+export default Base.extend(
{
settings: null,
@@ -107,7 +110,7 @@ Garnish.DisclosureMenu = Garnish.Base.extend(
handleKeypress: function(event) {
var keyCode = event.keyCode;
-
+
switch (keyCode) {
case Garnish.RIGHT_KEY:
case Garnish.DOWN_KEY:
@@ -149,14 +152,14 @@ Garnish.DisclosureMenu = Garnish.Base.extend(
'scroll',
'setContainerPosition'
);
-
+
this.$container.velocity('stop');
this.$container.css({
opacity: 1,
display: 'block',
});
-
+
// Set ARIA attribute for expanded
this.$trigger.attr('aria-expanded', 'true');
@@ -223,10 +226,10 @@ Garnish.DisclosureMenu = Garnish.Base.extend(
var topAdjustment = this._alignmentElementOffset.top - this._wrapperElementOffset.top;
var bottomAdjustment = this._alignmentElementOffset.bottom - this._wrapperElementOffset.bottom;
- var bottomClearanceExists =
+ var bottomClearanceExists =
bottomClearance >= this._menuHeight ||
(topClearance < this._menuHeight && bottomClearance >= topClearance);
-
+
if (bottomClearanceExists) {
this.$container.css({
top: 'calc(100% + ' + bottomAdjustment + 'px)',
@@ -288,7 +291,7 @@ Garnish.DisclosureMenu = Garnish.Base.extend(
_alignRight: function () {
var rightAdjustment = this._alignmentElementOffset.right - this._wrapperElementOffset.right;
-
+
this.$container.css({
left: 'unset',
right: - rightAdjustment + 'px',
diff --git a/packages/craftcms-garnish/src/Drag.js b/src/web/assets/garnish/src/Drag.js
similarity index 98%
rename from packages/craftcms-garnish/src/Drag.js
rename to src/web/assets/garnish/src/Drag.js
index 0427b8da1bd..8ee6aa2bf20 100644
--- a/packages/craftcms-garnish/src/Drag.js
+++ b/src/web/assets/garnish/src/Drag.js
@@ -1,11 +1,14 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import BaseDrag from './BaseDrag.js';
+import $ from 'jquery';
+
/**
* Drag class
*
* Builds on the BaseDrag class by "picking up" the selceted element(s),
* without worrying about what to do when an element is being dragged.
*/
-Garnish.Drag = Garnish.BaseDrag.extend(
+export default BaseDrag.extend(
{
targetItemWidth: null,
targetItemHeight: null,
diff --git a/packages/craftcms-garnish/src/DragDrop.js b/src/web/assets/garnish/src/DragDrop.js
similarity index 96%
rename from packages/craftcms-garnish/src/DragDrop.js
rename to src/web/assets/garnish/src/DragDrop.js
index bed77dc907f..4fd3117fff4 100644
--- a/packages/craftcms-garnish/src/DragDrop.js
+++ b/src/web/assets/garnish/src/DragDrop.js
@@ -1,11 +1,14 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Drag from './Drag.js';
+import $ from 'jquery';
+
/**
* Drag-and-drop class
*
* Builds on the Drag class by allowing you to set up "drop targets"
* which the dragged elemements can be dropped onto.
*/
-Garnish.DragDrop = Garnish.Drag.extend({
+export default Drag.extend({
$dropTargets: null,
$activeDropTarget: null,
@@ -117,5 +120,5 @@ Garnish.DragDrop = Garnish.Drag.extend({
dropTargets: null,
onDropTargetChange: $.noop,
activeDropTargetClass: 'active'
- }
- });
+ }
+});
diff --git a/packages/craftcms-garnish/src/DragMove.js b/src/web/assets/garnish/src/DragMove.js
similarity index 83%
rename from packages/craftcms-garnish/src/DragMove.js
rename to src/web/assets/garnish/src/DragMove.js
index 28229fc0142..5d6210568b4 100644
--- a/packages/craftcms-garnish/src/DragMove.js
+++ b/src/web/assets/garnish/src/DragMove.js
@@ -1,10 +1,11 @@
-/** global: Garnish */
+import BaseDrag from './BaseDrag.js';
+
/**
* Drag-to-move clas
*
* Builds on the BaseDrag class by simply moving the dragged element(s) along with the mouse.
*/
-Garnish.DragMove = Garnish.BaseDrag.extend(
+export default BaseDrag.extend(
{
onDrag: function(items, settings) {
this.$targetItem.css({
diff --git a/packages/craftcms-garnish/src/DragSort.js b/src/web/assets/garnish/src/DragSort.js
similarity index 99%
rename from packages/craftcms-garnish/src/DragSort.js
rename to src/web/assets/garnish/src/DragSort.js
index 902a40267ec..d0d525b0e26 100644
--- a/packages/craftcms-garnish/src/DragSort.js
+++ b/src/web/assets/garnish/src/DragSort.js
@@ -1,10 +1,13 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Drag from './Drag.js';
+import $ from 'jquery';
+
/**
* Drag-to-sort class
*
* Builds on the Drag class by allowing you to sort the elements amongst themselves.
*/
-Garnish.DragSort = Garnish.Drag.extend(
+export default Drag.extend(
{
$heightedContainer: null,
$insertion: null,
diff --git a/packages/craftcms-garnish/src/EscManager.js b/src/web/assets/garnish/src/EscManager.js
similarity index 92%
rename from packages/craftcms-garnish/src/EscManager.js
rename to src/web/assets/garnish/src/EscManager.js
index bdac4869b86..f3077fa3599 100644
--- a/packages/craftcms-garnish/src/EscManager.js
+++ b/src/web/assets/garnish/src/EscManager.js
@@ -1,9 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+
/**
* ESC key manager class
* @deprecated Use Garnish.ShortcutManager instead
*/
-Garnish.EscManager = Garnish.Base.extend(
+export default Base.extend(
{
handlers: null,
@@ -54,5 +56,3 @@ Garnish.EscManager = Garnish.Base.extend(
}
}
);
-
-Garnish.escManager = new Garnish.EscManager();
diff --git a/packages/craftcms-garnish/src/Garnish.js b/src/web/assets/garnish/src/Garnish.js
similarity index 83%
rename from packages/craftcms-garnish/src/Garnish.js
rename to src/web/assets/garnish/src/Garnish.js
index 6435be2ab9d..a0663078e2d 100644
--- a/packages/craftcms-garnish/src/Garnish.js
+++ b/src/web/assets/garnish/src/Garnish.js
@@ -1,3 +1,24 @@
+import $ from 'jquery';
+import Base from './Base.js';
+import BaseDrag from './BaseDrag.js';
+import CheckboxSelect from './CheckboxSelect.js';
+import ContextMenu from './ContextMenu.js';
+import CustomSelect from './CustomSelect.js';
+import DisclosureMenu from './DisclosureMenu.js';
+import Drag from './Drag.js';
+import DragDrop from './DragDrop.js';
+import DragMove from './DragMove.js';
+import DragSort from './DragSort.js';
+import EscManager from './EscManager.js';
+import HUD from './HUD.js';
+import MenuBtn from './MenuBtn.js';
+import MixedInput from './MixedInput.js';
+import Modal from './Modal.js';
+import NiceText from './NiceText.js';
+import Select from './Select.js';
+import SelectMenu from './SelectMenu.js';
+import ShortcutManager from './ShortcutManager.js';
+
/**
* @namespace Garnish
*/
@@ -7,8 +28,7 @@ if (typeof Garnish !== 'undefined') {
throw 'Garnish is already defined!';
}
-Garnish = {
-
+let Garnish = {
// jQuery objects for common elements
$win: $(window),
$doc: $(document),
@@ -103,7 +123,7 @@ Garnish = $.extend(Garnish, {
* @return {boolean}
*/
isJquery: function(val) {
- return (val instanceof jQuery);
+ return (val instanceof $);
},
/**
@@ -586,178 +606,26 @@ Garnish = $.extend(Garnish, {
}
});
-
-/**
- * Garnish base class
- */
-Garnish.Base = Base.extend({
-
- settings: null,
-
- _eventHandlers: null,
- _namespace: null,
- _$listeners: null,
- _disabled: false,
-
- constructor: function() {
- this._eventHandlers = [];
- this._namespace = '.Garnish' + Math.floor(Math.random() * 1000000000);
- this._listeners = [];
- this.init.apply(this, arguments);
- },
-
- init: $.noop,
-
- setSettings: function(settings, defaults) {
- var baseSettings = (typeof this.settings === 'undefined' ? {} : this.settings);
- this.settings = $.extend({}, baseSettings, defaults, settings);
- },
-
- on: function(events, data, handler) {
- if (typeof data === 'function') {
- handler = data;
- data = {};
- }
-
- events = Garnish._normalizeEvents(events);
-
- for (var i = 0; i < events.length; i++) {
- var ev = events[i];
- this._eventHandlers.push({
- type: ev[0],
- namespace: ev[1],
- data: data,
- handler: handler
- });
- }
- },
-
- off: function(events, handler) {
- events = Garnish._normalizeEvents(events);
-
- for (var i = 0; i < events.length; i++) {
- var ev = events[i];
-
- for (var j = this._eventHandlers.length - 1; j >= 0; j--) {
- var eventHandler = this._eventHandlers[j];
-
- if (
- eventHandler.type === ev[0] &&
- (!ev[1] || eventHandler.namespace === ev[1]) &&
- eventHandler.handler === handler
- ) {
- this._eventHandlers.splice(j, 1);
- }
- }
- }
- },
-
- trigger: function(type, data) {
- var ev = {
- type: type,
- target: this
- };
-
- // instance level event handlers
- var i, handler, _ev;
- for (i = 0; i < this._eventHandlers.length; i++) {
- handler = this._eventHandlers[i];
-
- if (handler.type === type) {
- _ev = $.extend({data: handler.data}, data, ev);
- handler.handler(_ev);
- }
- }
-
- // class level event handlers
- for (i = 0; i < Garnish._eventHandlers.length; i++) {
- handler = Garnish._eventHandlers[i];
-
- if (this instanceof handler.target && handler.type === type) {
- _ev = $.extend({data: handler.data}, data, ev);
- handler.handler(_ev);
- }
- }
- },
-
- _splitEvents: function(events) {
- if (typeof events === 'string') {
- events = events.split(',');
-
- for (var i = 0; i < events.length; i++) {
- events[i] = $.trim(events[i]);
- }
- }
-
- return events;
- },
-
- _formatEvents: function(events) {
- events = this._splitEvents(events).slice(0);
-
- for (var i = 0; i < events.length; i++) {
- events[i] += this._namespace;
- }
-
- return events.join(' ');
- },
-
- addListener: function(elem, events, data, func) {
- var $elem = $(elem);
-
- // Ignore if there aren't any elements
- if (!$elem.length) {
- return;
- }
-
- events = this._splitEvents(events);
-
- // Param mapping
- if (typeof func === 'undefined' && typeof data !== 'object') {
- // (elem, events, func)
- func = data;
- data = {};
- }
-
- if (typeof func === 'function') {
- func = func.bind(this);
- }
- else {
- func = this[func].bind(this);
- }
-
- $elem.on(this._formatEvents(events), data, $.proxy(function() {
- if (!this._disabled) {
- return func.apply(this, arguments);
- }
- }, this));
-
- // Remember that we're listening to this element
- if ($.inArray(elem, this._listeners) === -1) {
- this._listeners.push(elem);
- }
- },
-
- removeListener: function(elem, events) {
- $(elem).off(this._formatEvents(events));
- },
-
- removeAllListeners: function(elem) {
- $(elem).off(this._namespace);
- },
-
- disable: function() {
- this._disabled = true;
- },
-
- enable: function() {
- this._disabled = false;
- },
-
- destroy: function() {
- this.trigger('destroy');
- this.removeAllListeners(this._listeners);
- }
+Object.assign(Garnish, {
+ Base,
+ BaseDrag,
+ CheckboxSelect,
+ ContextMenu,
+ CustomSelect,
+ DisclosureMenu,
+ Drag,
+ DragDrop,
+ DragMove,
+ DragSort,
+ EscManager,
+ HUD,
+ MenuBtn,
+ MixedInput,
+ Modal,
+ NiceText,
+ Select,
+ SelectMenu,
+ ShortcutManager,
});
// Custom events
@@ -780,7 +648,7 @@ function triggerResizeEvent(elem) {
}
// Work them into jQuery's event system
-$.extend(jQuery.event.special, {
+$.extend($.event.special, {
activate: {
setup: function(data, namespaces, eventHandle) {
var activateNamespace = this._namespace + '-activate';
@@ -892,10 +760,12 @@ $.extend(jQuery.event.special, {
});
// Give them their own element collection chaining methods
-jQuery.each(['activate', 'textchange', 'resize'], function(i, name) {
- jQuery.fn[name] = function(data, fn) {
+$.each(['activate', 'textchange', 'resize'], function(i, name) {
+ $.fn[name] = function(data, fn) {
return arguments.length > 0 ?
this.on(name, null, data, fn) :
this.trigger(name);
};
});
+
+export default Garnish;
diff --git a/packages/craftcms-garnish/src/HUD.js b/src/web/assets/garnish/src/HUD.js
similarity index 99%
rename from packages/craftcms-garnish/src/HUD.js
rename to src/web/assets/garnish/src/HUD.js
index 232808734c7..86f316b2c29 100644
--- a/packages/craftcms-garnish/src/HUD.js
+++ b/src/web/assets/garnish/src/HUD.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* HUD
*/
-Garnish.HUD = Garnish.Base.extend(
+export default Base.extend(
{
$trigger: null,
$fixedTriggerParent: null,
diff --git a/packages/craftcms-garnish/src/MenuBtn.js b/src/web/assets/garnish/src/MenuBtn.js
similarity index 98%
rename from packages/craftcms-garnish/src/MenuBtn.js
rename to src/web/assets/garnish/src/MenuBtn.js
index 7bf16d4b61e..956296b7911 100644
--- a/packages/craftcms-garnish/src/MenuBtn.js
+++ b/src/web/assets/garnish/src/MenuBtn.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Menu Button
*/
-Garnish.MenuBtn = Garnish.Base.extend(
+export default Base.extend(
{
$btn: null,
menu: null,
diff --git a/packages/craftcms-garnish/src/MixedInput.js b/src/web/assets/garnish/src/MixedInput.js
similarity index 98%
rename from packages/craftcms-garnish/src/MixedInput.js
rename to src/web/assets/garnish/src/MixedInput.js
index a222f796191..2d2c5701f80 100644
--- a/packages/craftcms-garnish/src/MixedInput.js
+++ b/src/web/assets/garnish/src/MixedInput.js
@@ -1,10 +1,13 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Mixed input
*
* @todo RTL support, in the event that the input doesn't have dir="ltr".
*/
-Garnish.MixedInput = Garnish.Base.extend(
+export default Base.extend(
{
$container: null,
elements: null,
@@ -228,7 +231,7 @@ Garnish.MixedInput = Garnish.Base.extend(
});
-var TextElement = Garnish.Base.extend({
+var TextElement = Base.extend({
parentInput: null,
$input: null,
diff --git a/packages/craftcms-garnish/src/Modal.js b/src/web/assets/garnish/src/Modal.js
similarity index 98%
rename from packages/craftcms-garnish/src/Modal.js
rename to src/web/assets/garnish/src/Modal.js
index e16e85fc214..a88a7b0efe5 100644
--- a/packages/craftcms-garnish/src/Modal.js
+++ b/src/web/assets/garnish/src/Modal.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Modal
*/
-Garnish.Modal = Garnish.Base.extend(
+export default Base.extend(
{
$container: null,
$shade: null,
diff --git a/packages/craftcms-garnish/src/NiceText.js b/src/web/assets/garnish/src/NiceText.js
similarity index 98%
rename from packages/craftcms-garnish/src/NiceText.js
rename to src/web/assets/garnish/src/NiceText.js
index 5d20a4f68fe..ffae336ed77 100644
--- a/packages/craftcms-garnish/src/NiceText.js
+++ b/src/web/assets/garnish/src/NiceText.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Nice Text
*/
-Garnish.NiceText = Garnish.Base.extend(
+export default Base.extend(
{
$input: null,
$hint: null,
diff --git a/packages/craftcms-garnish/src/Select.js b/src/web/assets/garnish/src/Select.js
similarity index 99%
rename from packages/craftcms-garnish/src/Select.js
rename to src/web/assets/garnish/src/Select.js
index 25900a7dcaa..e21f8c0c7e4 100644
--- a/packages/craftcms-garnish/src/Select.js
+++ b/src/web/assets/garnish/src/Select.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Select
*/
-Garnish.Select = Garnish.Base.extend(
+export default Base.extend(
{
$container: null,
$items: null,
diff --git a/packages/craftcms-garnish/src/SelectMenu.js b/src/web/assets/garnish/src/SelectMenu.js
similarity index 92%
rename from packages/craftcms-garnish/src/SelectMenu.js
rename to src/web/assets/garnish/src/SelectMenu.js
index 031c5ebf6e3..340dbd1c264 100644
--- a/packages/craftcms-garnish/src/SelectMenu.js
+++ b/src/web/assets/garnish/src/SelectMenu.js
@@ -1,8 +1,11 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import CustomSelect from './CustomSelect.js';
+import $ from 'jquery';
+
/**
* Select Menu
*/
-Garnish.SelectMenu = Garnish.CustomSelect.extend(
+export default CustomSelect.extend(
{
/**
* Constructor
diff --git a/packages/craftcms-garnish/src/ShortcutManager.js b/src/web/assets/garnish/src/ShortcutManager.js
similarity index 95%
rename from packages/craftcms-garnish/src/ShortcutManager.js
rename to src/web/assets/garnish/src/ShortcutManager.js
index d98cdb5576d..71e544b1ed5 100644
--- a/packages/craftcms-garnish/src/ShortcutManager.js
+++ b/src/web/assets/garnish/src/ShortcutManager.js
@@ -1,11 +1,14 @@
-/** global: Garnish */
+import Garnish from './Garnish.js';
+import Base from './Base.js';
+import $ from 'jquery';
+
/**
* Keyboard shortcut manager class
*
* This can be used to map keyboard events to the current UI "layer" (whether that's the base document,
* a modal, an HUD, or a menu).
*/
-Garnish.ShortcutManager = Garnish.Base.extend(
+export default Base.extend(
{
shortcuts: null,
layer: 0,
@@ -93,5 +96,3 @@ Garnish.ShortcutManager = Garnish.Base.extend(
},
}
);
-
-Garnish.shortcutManager = new Garnish.ShortcutManager();
diff --git a/src/web/assets/garnish/src/index.js b/src/web/assets/garnish/src/index.js
new file mode 100644
index 00000000000..076d90f3902
--- /dev/null
+++ b/src/web/assets/garnish/src/index.js
@@ -0,0 +1,13 @@
+import Garnish from './Garnish.js';
+
+/**
+ * @deprecated
+ * Assign these here to avoid circular dependencies
+ */
+ Object.assign(Garnish, {
+ Menu: Garnish.CustomSelect,
+ escManager: new Garnish.EscManager(),
+ shortcutManager: new Garnish.ShortcutManager(),
+});
+
+export default Garnish;
diff --git a/packages/craftcms-garnish/lib/Base.js b/src/web/assets/garnish/src/lib/Base.js
similarity index 99%
rename from packages/craftcms-garnish/lib/Base.js
rename to src/web/assets/garnish/src/lib/Base.js
index 38b161911c3..7b5793aa317 100644
--- a/packages/craftcms-garnish/lib/Base.js
+++ b/src/web/assets/garnish/src/lib/Base.js
@@ -145,3 +145,5 @@ Base = Base.extend({
return String(this.valueOf());
}
});
+
+export default Base;
diff --git a/src/web/assets/garnish/webpack.config.js b/src/web/assets/garnish/webpack.config.js
new file mode 100644
index 00000000000..2099318acb8
--- /dev/null
+++ b/src/web/assets/garnish/webpack.config.js
@@ -0,0 +1,31 @@
+/* jshint esversion: 6 */
+/* globals module, require, __dirname */
+const { getConfig } = require('@craftcms/webpack');
+const {join} = require('path');
+
+module.exports = getConfig({
+ context: __dirname,
+ watchPaths: [join(__dirname, 'src')],
+ config: {
+ entry: {
+ garnish: './index.js',
+ },
+ module: {
+ rules: [
+ {
+ test: require.resolve('./src/index.js'),
+ loader: "expose-loader",
+ options: {
+ exposes: [
+ {
+ globalName: "Garnish",
+ moduleLocalName: "default",
+ }
+ ]
+ },
+ },
+ ],
+
+ }
+ }
+});
diff --git a/webpack.config.js b/webpack.config.js
index a47f8539859..b590bb590e7 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,7 +1,3 @@
const {getConfigs} = require('@craftcms/webpack');
-const garnishConfig = require('@craftcms/garnish');
-module.exports = [
- garnishConfig,
- ...getConfigs()
-];
+module.exports = getConfigs();