text: /**
   * Scrollable.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
   */
  
  /**
   * This mixin makes controls scrollable using custom scrollbars.
   *
   * @-x-less Scrollable.less
   * @mixin tinymce.ui.Scrollable
   */
  define(
    'tinymce.core.ui.Scrollable',
    [
      "tinymce.core.dom.DomQuery",
      "tinymce.core.ui.DragHelper"
    ],
    function ($, DragHelper) {
      "use strict";
  
      return {
        init: function () {
          var self = this;
          self.on('repaint', self.renderScroll);
        },
  
        renderScroll: function () {
          var self = this, margin = 2;
  
          function repaintScroll() {
            var hasScrollH, hasScrollV, bodyElm;
  
            function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
              var containerElm, scrollBarElm, scrollThumbElm;
              var containerSize, scrollSize, ratio, rect;
              var posNameLower, sizeNameLower;
  
              scrollBarElm = self.getEl('scroll' + axisName);
              if (scrollBarElm) {
                posNameLower = posName.toLowerCase();
                sizeNameLower = sizeName.toLowerCase();
  
                $(self.getEl('absend')).css(posNameLower, self.layoutRect()[contentSizeName] - 1);
  
                if (!hasScroll) {
                  $(scrollBarElm).css('display', 'none');
                  return;
                }
  
                $(scrollBarElm).css('display', 'block');
                containerElm = self.getEl('body');
                scrollThumbElm = self.getEl('scroll' + axisName + "t");
                containerSize = containerElm["client" + sizeName] - (margin * 2);
                containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
                scrollSize = containerElm["scroll" + sizeName];
                ratio = containerSize / scrollSize;
  
                rect = {};
                rect[posNameLower] = containerElm["offset" + posName] + margin;
                rect[sizeNameLower] = containerSize;
                $(scrollBarElm).css(rect);
  
                rect = {};
                rect[posNameLower] = containerElm["scroll" + posName] * ratio;
                rect[sizeNameLower] = containerSize * ratio;
                $(scrollThumbElm).css(rect);
              }
            }
  
            bodyElm = self.getEl('body');
            hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
            hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
  
            repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
            repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
          }
  
          function addScroll() {
            function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
              var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
  
              $(self.getEl()).append(
                '<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
                '<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
                '</div>'
              );
  
              self.draghelper = new DragHelper(axisId + 't', {
                start: function () {
                  scrollStart = self.getEl('body')["scroll" + posName];
                  $('#' + axisId).addClass(prefix + 'active');
                },
  
                drag: function (e) {
                  var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
  
                  hasScrollH = layoutRect.contentW > layoutRect.innerW;
                  hasScrollV = layoutRect.contentH > layoutRect.innerH;
                  containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
                  containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
  
                  ratio = containerSize / self.getEl('body')["scroll" + sizeName];
                  self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
                },
  
                stop: function () {
                  $('#' + axisId).removeClass(prefix + 'active');
                }
              });
            }
  
            self.classes.add('scroll');
  
            addScrollAxis("v", "Top", "Height", "Y", "Width");
            addScrollAxis("h", "Left", "Width", "X", "Height");
          }
  
          if (self.settings.autoScroll) {
            if (!self._hasScroll) {
              self._hasScroll = true;
              addScroll();
  
              self.on('wheel', function (e) {
                var bodyEl = self.getEl('body');
  
                bodyEl.scrollLeft += (e.deltaX || 0) * 10;
                bodyEl.scrollTop += e.deltaY * 10;
  
                repaintScroll();
              });
  
              $(self.getEl('body')).on("scroll", repaintScroll);
            }
  
            repaintScroll();
          }
        }
      };
    }
  );