Project import generated by Copybara.

GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/node_modules/mdl-ext/es/utils/dom-utils.js b/node_modules/mdl-ext/es/utils/dom-utils.js
new file mode 100644
index 0000000..3f63f90
--- /dev/null
+++ b/node_modules/mdl-ext/es/utils/dom-utils.js
@@ -0,0 +1,424 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.tether = exports.removeChildElements = exports.moveElements = exports.isRectInsideWindowViewport = exports.isFocusable = exports.getScrollParents = exports.getParentElements = exports.getWindowViewport = undefined;
+
+var _isNan = require('babel-runtime/core-js/number/is-nan');
+
+var _isNan2 = _interopRequireDefault(_isNan);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/**
+ * Remove child element(s)
+ * element.innerHTNL = '' has a performance penality!
+ * @see http://jsperf.com/empty-an-element/16
+ * @see http://jsperf.com/force-reflow
+ * @param element
+ * @param forceReflow
+ */
+var removeChildElements = function removeChildElements(element) {
+  var forceReflow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
+
+
+  // See: http://jsperf.com/empty-an-element/16
+  while (element.lastChild) {
+    element.removeChild(element.lastChild);
+  }
+  if (forceReflow) {
+    // See: http://jsperf.com/force-reflow
+    var d = element.style.display;
+
+    element.style.display = 'none';
+    element.style.display = d;
+  }
+};
+
+/**
+ * Moves child elements from a DOM node to another dom node.
+ * @param source {HTMLElement}
+ * @param target {HTMLElement} If the target parameter is ommited, a document fragment is created
+ * @return {HTMLElement} The target node
+ *
+ * @example
+ * // Moves child elements from a DOM node to another dom node.
+ * moveElements(source, destination);
+ *
+ * @example
+ * // If the second parameter is ommited, a document fragment is created:
+ * let fragment = moveElements(source);
+ *
+ * @See: https://github.com/webmodules/dom-move
+ */
+var moveElements = function moveElements(source, target) {
+  if (!target) {
+    target = source.ownerDocument.createDocumentFragment();
+  }
+  while (source.firstChild) {
+    target.appendChild(source.firstChild);
+  }
+  return target;
+};
+
+/**
+ * Get the browser viewport dimensions
+ * @see http://stackoverflow.com/questions/1248081/get-the-browser-viewport-dimensions-with-javascript
+ * @return {{windowWidth: number, windowHeight: number}}
+ */
+var getWindowViewport = function getWindowViewport() {
+  return {
+    viewportWidth: Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0),
+    viewportHeight: Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
+  };
+};
+
+/**
+ * Check whether an element is in the window viewport
+ * @see http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/
+ * @param top
+ * @param left
+ * @param bottom
+ * @param right
+ * @return {boolean} true if rectangle is inside window viewport, otherwise false
+ */
+var isRectInsideWindowViewport = function isRectInsideWindowViewport(_ref) {
+  var top = _ref.top,
+      left = _ref.left,
+      bottom = _ref.bottom,
+      right = _ref.right;
+
+  var _getWindowViewport = getWindowViewport(),
+      viewportWidth = _getWindowViewport.viewportWidth,
+      viewportHeight = _getWindowViewport.viewportHeight;
+
+  return top >= 0 && left >= 0 && bottom <= viewportHeight && right <= viewportWidth;
+};
+
+/**
+ * Get a list of parent elements that can possibly scroll
+ * @param el the element to get parents for
+ * @returns {Array}
+ */
+var getScrollParents = function getScrollParents(el) {
+  var elements = [];
+
+  /*
+  for (el = el.parentNode; el; el = el.parentNode) {
+    const cs = window.getComputedStyle(el);
+    if(!(cs.overflowY === 'hidden' && cs.overflowX === 'hidden')) {
+      elements.unshift(el);
+    }
+    if(el === document.body) {
+      break;
+    }
+  }
+  */
+
+  var element = el.parentNode;
+  while (element) {
+    var cs = window.getComputedStyle(element);
+    if (!(cs.overflowY === 'hidden' && cs.overflowX === 'hidden')) {
+      elements.unshift(element);
+    }
+    if (element === document.body) {
+      break;
+    }
+    element = element.parentNode;
+  }
+
+  return elements;
+};
+
+/**
+ * Get a list of parent elements, from a given element to a given element
+ * @param {HTMLElement} from
+ * @param {HTMLElement} to
+ * @return {Array<HTMLElement>} the parent elements, not including from and to
+ */
+var getParentElements = function getParentElements(from, to) {
+  var result = [];
+  var element = from.parentNode;
+  while (element) {
+    if (element === to) {
+      break;
+    }
+    result.unshift(element);
+    element = element.parentNode;
+  }
+  return result;
+};
+
+/**
+ * Position element next to button
+ *
+ * Positioning strategy
+ *  1. element.height > viewport.height
+ *     let element.height = viewport.heigt
+ *     let element.overflow-y = auto
+ *  2. element.width > viewport.width
+ *     let element.width = viewport.width
+ *  3. position element below button, align left edge of element with button left
+ *       done if element inside viewport
+ *  4. position element below button, align right edge of element with button right
+ *       done if element inside viewport
+ *  5. positions element above button, aligns left edge of element with button left
+ *       done if element inside viewport
+ *  6. position element above the control element, aligned to its right.
+ *       done if element inside viewport
+ *  7. position element at button right hand side, aligns element top with button top
+ *       done if element inside viewport
+ *  8. position element at button left hand side, aligns element top with button top
+ *       done if element inside viewport
+ *  9. position element inside viewport
+ *     1. position element at viewport bottom
+ *     2. position element at button right hand side
+ *        done if element inside viewport
+ *     3. position element at button left hand side
+ *       done if element inside viewport
+ *     4. position element at viewport right
+ * 10. done
+ *
+ */
+var tether = function tether(controlledBy, element) {
+  var controlRect = controlledBy.getBoundingClientRect();
+
+  // 1. will element height fit inside window viewport?
+
+  var _getWindowViewport2 = getWindowViewport(),
+      viewportWidth = _getWindowViewport2.viewportWidth,
+      viewportHeight = _getWindowViewport2.viewportHeight;
+
+  element.style.height = 'auto';
+  //element.style.overflowY = 'hidden';
+  if (element.offsetHeight > viewportHeight) {
+    element.style.height = viewportHeight + 'px';
+    element.style.overflowY = 'auto';
+  }
+
+  // 2. will element width fit inside window viewport?
+  element.style.width = 'auto';
+  if (element.offsetWidth > viewportWidth) {
+    element.style.width = viewportWidth + 'px';
+  }
+
+  var elementRect = element.getBoundingClientRect();
+
+  // element to control distance
+  var dy = controlRect.top - elementRect.top;
+  var dx = controlRect.left - elementRect.left;
+
+  // element rect, window coordinates relative to top,left of control
+  var top = elementRect.top + dy;
+  var left = elementRect.left + dx;
+  var bottom = top + elementRect.height;
+  var right = left + elementRect.width;
+
+  // Position relative to control
+  var ddy = dy;
+  var ddx = dx;
+
+  if (isRectInsideWindowViewport({
+    top: top + controlRect.height,
+    left: left,
+    bottom: bottom + controlRect.height,
+    right: right
+  })) {
+    // 3 position element below the control element, aligned to its left
+    ddy = controlRect.height + dy;
+    //console.log('***** 3');
+  } else if (isRectInsideWindowViewport({
+    top: top + controlRect.height,
+    left: left + controlRect.width - elementRect.width,
+    bottom: bottom + controlRect.height,
+    right: left + controlRect.width
+  })) {
+    // 4 position element below the control element, aligned to its right
+    ddy = controlRect.height + dy;
+    ddx = dx + controlRect.width - elementRect.width;
+    //console.log('***** 4');
+  } else if (isRectInsideWindowViewport({
+    top: top - elementRect.height,
+    left: left,
+    bottom: bottom - elementRect.height,
+    right: right
+  })) {
+    // 5. position element above the control element, aligned to its left.
+    ddy = dy - elementRect.height;
+    //console.log('***** 5');
+  } else if (isRectInsideWindowViewport({
+    top: top - elementRect.height,
+    left: left + controlRect.width - elementRect.width,
+    bottom: bottom - elementRect.height,
+    right: left + controlRect.width
+  })) {
+    // 6. position element above the control element, aligned to its right.
+    ddy = dy - elementRect.height;
+    ddx = dx + controlRect.width - elementRect.width;
+    //console.log('***** 6');
+  } else if (isRectInsideWindowViewport({
+    top: top,
+    left: left + controlRect.width,
+    bottom: bottom,
+    right: right + controlRect.width
+  })) {
+    // 7. position element at button right hand side
+    ddx = controlRect.width + dx;
+    //console.log('***** 7');
+  } else if (isRectInsideWindowViewport({
+    top: top,
+    left: left - controlRect.width,
+    bottom: bottom,
+    right: right - controlRect.width
+  })) {
+    // 8. position element at button left hand side
+    ddx = dx - elementRect.width;
+    //console.log('***** 8');
+  } else {
+    // 9. position element inside viewport, near controlrect if possible
+    //console.log('***** 9');
+
+    // 9.1 position element near controlrect bottom
+    ddy = dy - bottom + viewportHeight;
+    if (top + controlRect.height >= 0 && bottom + controlRect.height <= viewportHeight) {
+      ddy = controlRect.height + dy;
+    } else if (top - elementRect.height >= 0 && bottom - elementRect.height <= viewportHeight) {
+      ddy = dy - elementRect.height;
+    }
+
+    if (left + elementRect.width + controlRect.width <= viewportWidth) {
+      // 9.2 Position element at button right hand side
+      ddx = controlRect.width + dx;
+      //console.log('***** 9.2');
+    } else if (left - elementRect.width >= 0) {
+      // 9.3 Position element at button left hand side
+      ddx = dx - elementRect.width;
+      //console.log('***** 9.3');
+    } else {
+      // 9.4 position element at (near) viewport right
+      var r = left + elementRect.width - viewportWidth;
+      ddx = dx - r;
+      //console.log('***** 9.4');
+    }
+  }
+
+  // 10. done
+  element.style.top = element.offsetTop + ddy + 'px';
+  element.style.left = element.offsetLeft + ddx + 'px';
+  //console.log('***** 10. done');
+};
+
+/**
+ * Check if the given element can receive focus
+ * @param {HTMLElement} element the element to check
+ * @return {boolean} true if the element is focusable, otherwise false
+ */
+var isFocusable = function isFocusable(element) {
+  // https://github.com/stephenmathieson/is-focusable/blob/master/index.js
+  // http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
+
+  if (element.hasAttribute('tabindex')) {
+    var tabindex = element.getAttribute('tabindex');
+    if (!(0, _isNan2.default)(tabindex)) {
+      return parseInt(tabindex) > -1;
+    }
+  }
+
+  if (element.hasAttribute('contenteditable') && element.getAttribute('contenteditable') !== 'false') {
+    // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-contenteditable
+    return true;
+  }
+
+  // natively focusable, but only when enabled
+  var selector = /input|select|textarea|button|details/i;
+  var name = element.nodeName;
+  if (selector.test(name)) {
+    return element.type.toLowerCase() !== 'hidden' && !element.disabled;
+  }
+
+  // anchors and area must have an href
+  if (name === 'A' || name === 'AREA') {
+    return !!element.href;
+  }
+
+  if (name === 'IFRAME') {
+    // Check visible iframe
+    var cs = window.getComputedStyle(element);
+    return cs.getPropertyValue('display').toLowerCase() !== 'none';
+  }
+
+  return false;
+};
+
+/**
+ * Get a list of offset parents for given element
+ * @see https://www.benpickles.com/articles/51-finding-a-dom-nodes-common-ancestor-using-javascript
+ * @param el the element
+ * @return {Array} a list of offset parents
+ */
+/*
+const offsetParents = (el) => {
+  const elements = [];
+  for (; el; el = el.offsetParent) {
+    elements.unshift(el);
+  }
+  if(!elements.find(e => e === document.body)) {
+    elements.unshift(document.body);
+  }
+  return elements;
+};
+*/
+
+/**
+ * Finds the common offset ancestor of two DOM nodes
+ * @see https://www.benpickles.com/articles/51-finding-a-dom-nodes-common-ancestor-using-javascript
+ * @see https://gist.github.com/benpickles/4059636
+ * @param a
+ * @param b
+ * @return {Element} The common offset ancestor of a and b
+ */
+/*
+const commonOffsetAncestor = (a, b) => {
+  const parentsA = offsetParents(a);
+  const parentsB = offsetParents(b);
+
+  for (let i = 0; i < parentsA.length; i++) {
+    if (parentsA[i] !== parentsB[i]) return parentsA[i-1];
+  }
+};
+*/
+
+/**
+ * Calculate position relative to a target element
+ * @see http://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
+ * @param target
+ * @param el
+ * @return {{top: number, left: number}}
+ */
+/*
+const calcPositionRelativeToTarget = (target, el) => {
+  let top = 0;
+  let left = 0;
+
+  while(el) {
+    top += (el.offsetTop - el.scrollTop + el.clientTop) || 0;
+    left += (el.offsetLeft - el.scrollLeft + el.clientLeft) || 0;
+    el = el.offsetParent;
+
+    if(el === target) {
+      break;
+    }
+  }
+  return { top: top, left: left };
+};
+*/
+
+exports.getWindowViewport = getWindowViewport;
+exports.getParentElements = getParentElements;
+exports.getScrollParents = getScrollParents;
+exports.isFocusable = isFocusable;
+exports.isRectInsideWindowViewport = isRectInsideWindowViewport;
+exports.moveElements = moveElements;
+exports.removeChildElements = removeChildElements;
+exports.tether = tether;
\ No newline at end of file