text: /**
   * Movable.js
   *
   * Released under LGPL License.
   * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
   *
   * License: http://www.tinymce.com/license
   * Contributing: http://www.tinymce.com/contributing
   */
  
  /**
   * Movable mixin. Makes controls movable absolute and relative to other elements.
   *
   * @mixin tinymce.ui.Movable
   */
  define(
    'tinymce.core.ui.Movable',
    [
      "tinymce.core.ui.DomUtils"
    ],
    function (DomUtils) {
      "use strict";
  
      function calculateRelativePosition(ctrl, targetElm, rel) {
        var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
  
        viewport = DomUtils.getViewPort();
  
        // Get pos of target
        pos = DomUtils.getPos(targetElm);
        x = pos.x;
        y = pos.y;
  
        if (ctrl.state.get('fixed') && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') {
          x -= viewport.x;
          y -= viewport.y;
        }
  
        // Get size of self
        ctrlElm = ctrl.getEl();
        size = DomUtils.getSize(ctrlElm);
        selfW = size.width;
        selfH = size.height;
  
        // Get size of target
        size = DomUtils.getSize(targetElm);
        targetW = size.width;
        targetH = size.height;
  
        // Parse align string
        rel = (rel || '').split('');
  
        // Target corners
        if (rel[0] === 'b') {
          y += targetH;
        }
  
        if (rel[1] === 'r') {
          x += targetW;
        }
  
        if (rel[0] === 'c') {
          y += Math.round(targetH / 2);
        }
  
        if (rel[1] === 'c') {
          x += Math.round(targetW / 2);
        }
  
        // Self corners
        if (rel[3] === 'b') {
          y -= selfH;
        }
  
        if (rel[4] === 'r') {
          x -= selfW;
        }
  
        if (rel[3] === 'c') {
          y -= Math.round(selfH / 2);
        }
  
        if (rel[4] === 'c') {
          x -= Math.round(selfW / 2);
        }
  
        return {
          x: x,
          y: y,
          w: selfW,
          h: selfH
        };
      }
  
      return {
        /**
         * Tests various positions to get the most suitable one.
         *
         * @method testMoveRel
         * @param {DOMElement} elm Element to position against.
         * @param {Array} rels Array with relative positions.
         * @return {String} Best suitable relative position.
         */
        testMoveRel: function (elm, rels) {
          var viewPortRect = DomUtils.getViewPort();
  
          for (var i = 0; i < rels.length; i++) {
            var pos = calculateRelativePosition(this, elm, rels[i]);
  
            if (this.state.get('fixed')) {
              if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
                return rels[i];
              }
            } else {
              if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
                pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
                return rels[i];
              }
            }
          }
  
          return rels[0];
        },
  
        /**
         * Move relative to the specified element.
         *
         * @method moveRel
         * @param {Element} elm Element to move relative to.
         * @param {String} rel Relative mode. For example: br-tl.
         * @return {tinymce.ui.Control} Current control instance.
         */
        moveRel: function (elm, rel) {
          if (typeof rel != 'string') {
            rel = this.testMoveRel(elm, rel);
          }
  
          var pos = calculateRelativePosition(this, elm, rel);
          return this.moveTo(pos.x, pos.y);
        },
  
        /**
         * Move by a relative x, y values.
         *
         * @method moveBy
         * @param {Number} dx Relative x position.
         * @param {Number} dy Relative y position.
         * @return {tinymce.ui.Control} Current control instance.
         */
        moveBy: function (dx, dy) {
          var self = this, rect = self.layoutRect();
  
          self.moveTo(rect.x + dx, rect.y + dy);
  
          return self;
        },
  
        /**
         * Move to absolute position.
         *
         * @method moveTo
         * @param {Number} x Absolute x position.
         * @param {Number} y Absolute y position.
         * @return {tinymce.ui.Control} Current control instance.
         */
        moveTo: function (x, y) {
          var self = this;
  
          // TODO: Move this to some global class
          function constrain(value, max, size) {
            if (value < 0) {
              return 0;
            }
  
            if (value + size > max) {
              value = max - size;
              return value < 0 ? 0 : value;
            }
  
            return value;
          }
  
          if (self.settings.constrainToViewport) {
            var viewPortRect = DomUtils.getViewPort(window);
            var layoutRect = self.layoutRect();
  
            x = constrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
            y = constrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
          }
  
          if (self.state.get('rendered')) {
            self.layoutRect({ x: x, y: y }).repaint();
          } else {
            self.settings.x = x;
            self.settings.y = y;
          }
  
          self.fire('move', { x: x, y: y });
  
          return self;
        }
      };
    }
  );