| 'use strict'; |
| |
| var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); |
| |
| var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); |
| |
| var _createClass2 = require('babel-runtime/helpers/createClass'); |
| |
| var _createClass3 = _interopRequireDefault(_createClass2); |
| |
| var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); |
| |
| var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); |
| |
| var _stringUtils = require('../utils/string-utils'); |
| |
| var _fullThrottle = require('../utils/full-throttle'); |
| |
| var _fullThrottle2 = _interopRequireDefault(_fullThrottle); |
| |
| var _constants = require('../utils/constants'); |
| |
| var _domUtils = require('../utils/dom-utils'); |
| |
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
| |
| /** |
| * @license |
| * Copyright 2016 Leif Olsen. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * This code is built with Google Material Design Lite, |
| * which is Licensed under the Apache License, Version 2.0 |
| */ |
| |
| /** |
| * A menu button is a button that opens a menu. It is often styled as a |
| * typical push button with a downward pointing arrow or triangle to hint |
| * that activating the button will display a menu. |
| */ |
| var JS_MENU_BUTTON = 'mdlext-js-menu-button'; |
| var MENU_BUTTON_MENU = 'mdlext-menu'; |
| var MENU_BUTTON_MENU_ITEM = 'mdlext-menu__item'; |
| var MENU_BUTTON_MENU_ITEM_SEPARATOR = 'mdlext-menu__item-separator'; |
| //const MDL_LAYOUT_CONTENT = 'mdl-layout__content'; |
| |
| /** |
| * Creates the menu controlled by the menu button |
| * @param element |
| * @return {{element: Element, selected: Element, open: (function(*=)), removeListeners: (function()), downgrade: (function())}} |
| */ |
| |
| var menuFactory = function menuFactory(element) { |
| |
| var ariaControls = null; |
| var parentNode = null; |
| |
| var removeAllSelected = function removeAllSelected() { |
| [].concat((0, _toConsumableArray3.default)(element.querySelectorAll('.' + MENU_BUTTON_MENU_ITEM + '[aria-selected="true"]'))).forEach(function (selectedItem) { |
| return selectedItem.removeAttribute('aria-selected'); |
| }); |
| }; |
| |
| var setSelected = function setSelected(item) { |
| var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; |
| |
| if (force || item && !item.hasAttribute('aria-selected')) { |
| removeAllSelected(); |
| if (item) { |
| item.setAttribute('aria-selected', 'true'); |
| } |
| } |
| }; |
| |
| var getSelected = function getSelected() { |
| return element.querySelector('.' + MENU_BUTTON_MENU_ITEM + '[aria-selected="true"]'); |
| }; |
| |
| var isDisabled = function isDisabled(item) { |
| return item && item.hasAttribute('disabled'); |
| }; |
| |
| var isSeparator = function isSeparator(item) { |
| return item && item.classList.contains(MENU_BUTTON_MENU_ITEM_SEPARATOR); |
| }; |
| |
| var focus = function focus(item) { |
| if (item) { |
| item = item.closest('.' + MENU_BUTTON_MENU_ITEM); |
| } |
| if (item) { |
| item.focus(); |
| } |
| }; |
| |
| var nextItem = function nextItem(current) { |
| var n = current.nextElementSibling; |
| if (!n) { |
| n = element.firstElementChild; |
| } |
| if (!isDisabled(n) && !isSeparator(n)) { |
| focus(n); |
| } else { |
| var i = element.children.length; |
| while (n && i-- > 0) { |
| if (isDisabled(n) || isSeparator(n)) { |
| n = n.nextElementSibling; |
| if (!n) { |
| n = element.firstElementChild; |
| } |
| } else { |
| focus(n); |
| break; |
| } |
| } |
| } |
| }; |
| |
| var previousItem = function previousItem(current) { |
| var p = current.previousElementSibling; |
| if (!p) { |
| p = element.lastElementChild; |
| } |
| if (!isDisabled(p) && !isSeparator(p)) { |
| focus(p); |
| } else { |
| var i = element.children.length; |
| while (p && i-- > 0) { |
| if (isDisabled(p) || isSeparator(p)) { |
| p = p.previousElementSibling; |
| if (!p) { |
| p = element.lastElementChild; |
| } |
| } else { |
| focus(p); |
| break; |
| } |
| } |
| } |
| }; |
| |
| var firstItem = function firstItem() { |
| var item = element.firstElementChild; |
| if (isDisabled(item) || isSeparator(item)) { |
| nextItem(item); |
| } else { |
| focus(item); |
| } |
| }; |
| |
| var lastItem = function lastItem() { |
| var item = element.lastElementChild; |
| if (isDisabled(item) || isSeparator(item)) { |
| previousItem(item); |
| } else { |
| focus(item); |
| } |
| }; |
| |
| var selectItem = function selectItem(item) { |
| if (item && !isDisabled(item) && !isSeparator(item)) { |
| setSelected(item); |
| close(true, item); |
| } |
| }; |
| |
| var keyDownHandler = function keyDownHandler(event) { |
| |
| var item = event.target.closest('.' + MENU_BUTTON_MENU_ITEM); |
| |
| switch (event.keyCode) { |
| case _constants.VK_ARROW_UP: |
| case _constants.VK_ARROW_LEFT: |
| if (item) { |
| previousItem(item); |
| } else { |
| firstItem(); |
| } |
| break; |
| |
| case _constants.VK_ARROW_DOWN: |
| case _constants.VK_ARROW_RIGHT: |
| if (item) { |
| nextItem(item); |
| } else { |
| lastItem(); |
| } |
| break; |
| |
| case _constants.VK_HOME: |
| firstItem(); |
| break; |
| |
| case _constants.VK_END: |
| lastItem(); |
| break; |
| |
| case _constants.VK_SPACE: |
| case _constants.VK_ENTER: |
| selectItem(item); |
| break; |
| |
| case _constants.VK_ESC: |
| close(true); |
| break; |
| |
| case _constants.VK_TAB: |
| // We do not have a "natural" tab order from menu, so the best we can do is to set focus back to the button |
| close(true); |
| break; |
| |
| default: |
| return; |
| } |
| event.preventDefault(); |
| }; |
| |
| var blurHandler = function blurHandler(event) { |
| |
| // See: https://github.com/facebook/react/issues/2011 |
| var t = event.relatedTarget || event.explicitOriginalTarget || // FF |
| document.activeElement; // IE11 |
| |
| //console.log('***** blur, target, relatedTarget', event.target, t); |
| |
| try { |
| if (t) { |
| if (t.closest('.' + MENU_BUTTON_MENU) !== element && shouldClose(t)) { |
| close(); |
| } |
| } else { |
| close(); |
| } |
| } catch (err) { |
| // FF throws error: "TypeError: n.closest is not a function" if related target is a text node |
| close(); |
| } |
| }; |
| |
| var clickHandler = function clickHandler(event) { |
| //console.log('***** click, target', event.target); |
| |
| event.preventDefault(); |
| var t = event.target; |
| if (t && t.closest('.' + MENU_BUTTON_MENU) === element) { |
| var item = t.closest('.' + MENU_BUTTON_MENU_ITEM); |
| if (item) { |
| selectItem(item); |
| } |
| } else { |
| if (shouldClose(t)) { |
| close(); |
| } |
| } |
| }; |
| |
| var touchStartHandler = function touchStartHandler(event) { |
| //console.log('***** touchStart, target', event.target); |
| |
| var t = event.target; |
| if (!(t && t.closest('.' + MENU_BUTTON_MENU) === element)) { |
| if (event.type === 'touchstart') { |
| event.preventDefault(); |
| } |
| close(); |
| } |
| }; |
| |
| var addListeners = function addListeners() { |
| element.addEventListener('keydown', keyDownHandler, false); |
| element.addEventListener('blur', blurHandler, true); |
| element.addEventListener('click', clickHandler, true); |
| document.documentElement.addEventListener('touchstart', touchStartHandler, true); |
| }; |
| |
| var _removeListeners = function _removeListeners() { |
| element.removeEventListener('keydown', keyDownHandler, false); |
| element.removeEventListener('blur', blurHandler, true); |
| element.removeEventListener('click', clickHandler, true); |
| document.documentElement.removeEventListener('touchstart', touchStartHandler, true); |
| }; |
| |
| var _open = function _open(controlElement) { |
| var position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'first'; |
| |
| |
| ariaControls = controlElement.closest('.' + JS_MENU_BUTTON); |
| |
| element.style['min-width'] = Math.max(124, controlElement.getBoundingClientRect().width) + 'px'; |
| element.removeAttribute('hidden'); |
| (0, _domUtils.tether)(controlElement, element); |
| |
| var item = void 0; |
| switch (position.toLowerCase()) { |
| case 'first': |
| firstItem(); |
| break; |
| |
| case 'last': |
| lastItem(); |
| break; |
| |
| case 'selected': |
| item = getSelected(); |
| if (item && !item.hasAttribute('disabled')) { |
| focus(item); |
| } else { |
| firstItem(); |
| } |
| break; |
| } |
| |
| addListeners(); |
| }; |
| |
| var shouldClose = function shouldClose(target) { |
| //console.log('***** shouldClose'); |
| |
| var result = false; |
| var btn = target && target.closest('.' + JS_MENU_BUTTON) || null; |
| if (!btn) { |
| result = true; |
| } else if (btn.getAttribute('aria-controls') === element.id) { |
| if (btn !== ariaControls) { |
| result = true; |
| } |
| } else { |
| result = true; |
| } |
| return result; |
| }; |
| |
| var close = function close() { |
| var forceFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; |
| var item = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; |
| |
| _removeListeners(); |
| |
| element.dispatchEvent(new CustomEvent('_closemenu', { |
| bubbles: true, |
| cancelable: true, |
| detail: { forceFocus: forceFocus, item: item } |
| })); |
| }; |
| |
| var addWaiAria = function addWaiAria() { |
| if (!element.hasAttribute('id')) { |
| // Generate a random id |
| element.id = 'menu-button-' + (0, _stringUtils.randomString)(); |
| } |
| element.setAttribute('tabindex', '-1'); |
| element.setAttribute('role', 'menu'); |
| element.setAttribute('hidden', ''); |
| |
| [].concat((0, _toConsumableArray3.default)(element.querySelectorAll('.' + MENU_BUTTON_MENU_ITEM))).forEach(function (menuitem) { |
| menuitem.setAttribute('tabindex', '-1'); |
| menuitem.setAttribute('role', 'menuitem'); |
| }); |
| |
| [].concat((0, _toConsumableArray3.default)(element.querySelectorAll('.' + MENU_BUTTON_MENU_ITEM_SEPARATOR))).forEach(function (menuitem) { |
| menuitem.setAttribute('role', 'separator'); |
| }); |
| }; |
| |
| var init = function init() { |
| addWaiAria(); |
| parentNode = element.parentNode; |
| element.classList.add('is-upgraded'); |
| }; |
| |
| var _downgrade = function _downgrade() { |
| _removeListeners(); |
| if (element.parentNode !== parentNode) { |
| parentNode.appendChild(element); |
| } |
| element.classList.remove('is-upgraded'); |
| }; |
| |
| init(); |
| |
| return { |
| /** |
| * Get the menu element |
| * @returns {Element} the menu element |
| */ |
| get element() { |
| return element; |
| }, |
| |
| /** |
| * Set selected menu item |
| * @param item |
| */ |
| set selected(item) { |
| setSelected(item, true); |
| }, |
| |
| /** |
| * Open menu |
| * @param {Element} controlElement the element where the menu should be aligned to |
| * @param {String} position menuElement item to receive focus after menu element is opened |
| */ |
| open: function open(controlElement) { |
| var position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'first'; |
| return _open(controlElement, position); |
| }, |
| |
| /** |
| * Remove event listeners. |
| */ |
| removeListeners: function removeListeners() { |
| return _removeListeners(); |
| }, |
| |
| /** |
| * Downgrade menu |
| */ |
| downgrade: function downgrade() { |
| return _downgrade(); |
| } |
| }; |
| }; |
| |
| /** |
| * The menubutton component |
| */ |
| |
| var MenuButton = function () { |
| function MenuButton(element) { |
| var _this = this; |
| |
| (0, _classCallCheck3.default)(this, MenuButton); |
| |
| this.keyDownHandler = function (event) { |
| if (!_this.isDisabled()) { |
| switch (event.keyCode) { |
| case _constants.VK_ARROW_UP: |
| _this.openMenu('last'); |
| break; |
| |
| case _constants.VK_ARROW_DOWN: |
| _this.openMenu(); |
| break; |
| |
| case _constants.VK_SPACE: |
| case _constants.VK_ENTER: |
| _this.openMenu('selected'); |
| break; |
| |
| case _constants.VK_ESC: |
| _this.closeMenu(); |
| break; |
| |
| case _constants.VK_TAB: |
| _this.closeMenu(); |
| return; |
| |
| default: |
| return; |
| } |
| } |
| //event.stopPropagation(); |
| event.preventDefault(); |
| }; |
| |
| this.clickHandler = function () { |
| if (!_this.isDisabled()) { |
| if (_this.element.getAttribute('aria-expanded').toLowerCase() === 'true') { |
| _this.closeMenu(true); |
| } else { |
| _this.openMenu('selected'); |
| } |
| } |
| }; |
| |
| this.recalcMenuPosition = (0, _fullThrottle2.default)(function () { |
| var c = _this.focusElement.getBoundingClientRect(); |
| var dx = _this.focusElementLastScrollPosition.left - c.left; |
| var dy = _this.focusElementLastScrollPosition.top - c.top; |
| var left = (parseFloat(_this.menu.element.style.left) || 0) - dx; |
| var top = (parseFloat(_this.menu.element.style.top) || 0) - dy; |
| |
| _this.menu.element.style.left = left + 'px'; |
| _this.menu.element.style.top = top + 'px'; |
| _this.focusElementLastScrollPosition = c; |
| }); |
| |
| this.positionChangeHandler = function () { |
| _this.recalcMenuPosition(_this); |
| }; |
| |
| this.closeMenuHandler = function (event) { |
| if (event && event.detail) { |
| if (event.detail.item && event.detail.item !== _this.selectedItem) { |
| _this.selectedItem = event.detail.item; |
| _this.dispatchMenuSelect(); |
| } |
| _this.closeMenu(event.detail.forceFocus); |
| } |
| }; |
| |
| this.element = element; |
| this.focusElement = undefined; |
| this.focusElementLastScrollPosition = undefined; |
| this.scrollElements = []; |
| this.menu = undefined; |
| this.selectedItem = null; |
| this.init(); |
| } |
| |
| /** |
| * Re-position menu if content is scrolled, window is resized or orientation change |
| * @see https://javascriptweblog.wordpress.com/2015/11/02/of-classes-and-arrow-functions-a-cautionary-tale/ |
| */ |
| |
| |
| (0, _createClass3.default)(MenuButton, [{ |
| key: 'dispatchMenuSelect', |
| value: function dispatchMenuSelect() { |
| this.element.dispatchEvent(new CustomEvent('menuselect', { |
| bubbles: true, |
| cancelable: true, |
| detail: { source: this.selectedItem } |
| })); |
| } |
| }, { |
| key: 'isDisabled', |
| value: function isDisabled() { |
| return this.element.hasAttribute('disabled'); |
| } |
| }, { |
| key: 'removeListeners', |
| value: function removeListeners() { |
| this.element.removeEventListener('keydown', this.keyDownHandler); |
| this.element.removeEventListener('click', this.clickHandler); |
| } |
| }, { |
| key: 'openMenu', |
| value: function openMenu() { |
| var _this2 = this; |
| |
| var position = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'first'; |
| |
| |
| if (!this.isDisabled() && this.menu) { |
| |
| // Close the menu if button position change |
| this.scrollElements = (0, _domUtils.getScrollParents)(this.element); |
| this.scrollElements.forEach(function (el) { |
| return el.addEventListener('scroll', _this2.positionChangeHandler); |
| }); |
| |
| window.addEventListener('resize', this.positionChangeHandler); |
| window.addEventListener('orientationchange', this.positionChangeHandler); |
| this.menu.element.addEventListener('_closemenu', this.closeMenuHandler); |
| |
| this.menu.selected = this.selectedItem; |
| this.menu.open(this.focusElement, position); |
| this.element.setAttribute('aria-expanded', 'true'); |
| |
| this.focusElementLastScrollPosition = this.focusElement.getBoundingClientRect(); |
| } |
| } |
| }, { |
| key: 'closeMenu', |
| value: function closeMenu() { |
| var _this3 = this; |
| |
| var forceFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; |
| |
| if (this.menu) { |
| this.menu.removeListeners(); |
| this.scrollElements.forEach(function (el) { |
| return el.removeEventListener('scroll', _this3.positionChangeHandler); |
| }); |
| window.removeEventListener('resize', this.positionChangeHandler); |
| window.removeEventListener('orientationchange', this.positionChangeHandler); |
| this.menu.element.removeEventListener('_closemenu', this.closeMenuHandler); |
| |
| if (forceFocus) { |
| this.focus(); |
| } |
| this.element.setAttribute('aria-expanded', 'false'); |
| this.menu.element.setAttribute('hidden', ''); |
| } |
| } |
| }, { |
| key: 'focus', |
| value: function focus() { |
| if (!this.isDisabled()) { |
| this.focusElement.focus(); |
| } |
| } |
| }, { |
| key: 'init', |
| value: function init() { |
| var _this4 = this; |
| |
| var addListeners = function addListeners() { |
| _this4.element.addEventListener('keydown', _this4.keyDownHandler); |
| _this4.element.addEventListener('click', _this4.clickHandler); |
| }; |
| |
| var addWaiAria = function addWaiAria() { |
| _this4.element.setAttribute('role', 'button'); |
| _this4.element.setAttribute('aria-expanded', 'false'); |
| _this4.element.setAttribute('aria-haspopup', 'true'); |
| }; |
| |
| var addFocusElement = function addFocusElement() { |
| _this4.focusElement = _this4.element.querySelector('input[type="text"]'); |
| if (!_this4.focusElement) { |
| _this4.focusElement = _this4.element; |
| |
| if (!(_this4.focusElement.tagName.toLowerCase() === 'button' || _this4.focusElement.tagName.toLowerCase() === 'input')) { |
| if (!_this4.focusElement.hasAttribute('tabindex')) { |
| _this4.focusElement.setAttribute('tabindex', '0'); |
| } |
| } |
| } |
| }; |
| |
| var moveElementToDocumentBody = function moveElementToDocumentBody(element) { |
| // To position an element on top of all other z-indexed elements, the element should be moved to document.body |
| // See: https://philipwalton.com/articles/what-no-one-told-you-about-z-index/ |
| |
| if (element.parentNode !== document.body) { |
| return document.body.appendChild(element); |
| } |
| return element; |
| }; |
| |
| var findMenuElement = function findMenuElement() { |
| var menuElement = void 0; |
| var menuElementId = _this4.element.getAttribute('aria-controls'); |
| if (menuElementId !== null) { |
| menuElement = document.querySelector('#' + menuElementId); |
| } else { |
| menuElement = _this4.element.parentNode.querySelector('.' + MENU_BUTTON_MENU); |
| } |
| return menuElement; |
| }; |
| |
| var addMenu = function addMenu() { |
| var menuElement = findMenuElement(); |
| if (menuElement) { |
| if (menuElement.componentInstance) { |
| _this4.menu = menuElement.componentInstance; |
| } else { |
| _this4.menu = menuFactory(menuElement); |
| menuElement.componentInstance = _this4.menu; |
| moveElementToDocumentBody(menuElement); |
| } |
| _this4.element.setAttribute('aria-controls', _this4.menu.element.id); |
| } |
| }; |
| |
| addFocusElement(); |
| addWaiAria(); |
| addMenu(); |
| this.removeListeners(); |
| addListeners(); |
| } |
| }, { |
| key: 'downgrade', |
| value: function downgrade() { |
| var _this5 = this; |
| |
| if (this.menu) { |
| // Do not downgrade menu if there are other buttons sharing this menu |
| var related = [].concat((0, _toConsumableArray3.default)(document.querySelectorAll('.' + JS_MENU_BUTTON + '[aria-controls="' + this.element.getAttribute('aria-controls') + '"]'))); |
| if (related.filter(function (c) { |
| return c !== _this5.element && c.getAttribute('data-upgraded').indexOf('MaterialExtMenuButton') >= 0; |
| }).length === 0) { |
| this.menu.downgrade(); |
| } |
| } |
| this.removeListeners(); |
| } |
| }]); |
| return MenuButton; |
| }(); |
| |
| (function () { |
| 'use strict'; |
| |
| /** |
| * https://github.com/google/material-design-lite/issues/4205 |
| * @constructor |
| * @param {Element} element The element that will be upgraded. |
| */ |
| |
| var MaterialExtMenuButton = function MaterialExtMenuButton(element) { |
| this.element_ = element; |
| this.menuButton_ = null; |
| |
| // Initialize instance. |
| this.init(); |
| }; |
| window['MaterialExtMenuButton'] = MaterialExtMenuButton; |
| |
| // Public methods. |
| |
| /** |
| * Get the menu element controlled by this button, null if no menu is controlled by this button |
| * @public |
| */ |
| MaterialExtMenuButton.prototype.getMenuElement = function () { |
| return this.menuButton_.menu ? this.menuButton_.menu.element : null; |
| }; |
| MaterialExtMenuButton.prototype['getMenuElement'] = MaterialExtMenuButton.prototype.getMenuElement; |
| |
| /** |
| * Open menu |
| * @public |
| * @param {String} position one of "first", "last" or "selected" |
| */ |
| MaterialExtMenuButton.prototype.openMenu = function (position) { |
| this.menuButton_.openMenu(position); |
| }; |
| MaterialExtMenuButton.prototype['openMenu'] = MaterialExtMenuButton.prototype.openMenu; |
| |
| /** |
| * Close menu |
| * @public |
| */ |
| MaterialExtMenuButton.prototype.closeMenu = function () { |
| this.menuButton_.closeMenu(true); |
| }; |
| MaterialExtMenuButton.prototype['closeMenu'] = MaterialExtMenuButton.prototype.closeMenu; |
| |
| /** |
| * Get selected menu item |
| * @public |
| * @returns {Element} The selected menu item or null if no item selected |
| */ |
| MaterialExtMenuButton.prototype.getSelectedMenuItem = function () { |
| return this.menuButton_.selectedItem; |
| }; |
| MaterialExtMenuButton.prototype['getSelectedMenuItem'] = MaterialExtMenuButton.prototype.getSelectedMenuItem; |
| |
| /** |
| * Set (default) selected menu item |
| * @param {Element} item |
| */ |
| MaterialExtMenuButton.prototype.setSelectedMenuItem = function (item) { |
| this.menuButton_.selectedItem = item; |
| }; |
| MaterialExtMenuButton.prototype['setSelectedMenuItem'] = MaterialExtMenuButton.prototype.setSelectedMenuItem; |
| |
| /** |
| * Initialize component |
| */ |
| MaterialExtMenuButton.prototype.init = function () { |
| if (this.element_) { |
| this.menuButton_ = new MenuButton(this.element_); |
| this.element_.addEventListener('mdl-componentdowngraded', this.mdlDowngrade_.bind(this)); |
| this.element_.classList.add(_constants.IS_UPGRADED); |
| } |
| }; |
| |
| /** |
| * Downgrade component |
| * E.g remove listeners and clean up resources |
| */ |
| MaterialExtMenuButton.prototype.mdlDowngrade_ = function () { |
| this.menuButton_.downgrade(); |
| }; |
| |
| // The component registers itself. It can assume componentHandler is available |
| // in the global scope. |
| /* eslint no-undef: 0 */ |
| componentHandler.register({ |
| constructor: MaterialExtMenuButton, |
| classAsString: 'MaterialExtMenuButton', |
| cssClass: JS_MENU_BUTTON, |
| widget: true |
| }); |
| })(); |