Source: layout/HeightMixin.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This mixin module provides functions for setting the height of a widget based on a configured 
 * [heightMode]{@link module:alfresco/layout/HeightMixin#heightMode} attribute. By default the standard
 * approach is for the height to consume the available space from it's initial position down to the
 * bottom of the screen (assuming the page is initially displayed from the start of the page). However
 * it is also possible to specify that the height should be taken from a 
 * [dialog]{@link module:alfresco/dialogs/AlfDialog} in which the widget is placed or for it to be
 * given a specific height or (if a negative value is provided) for a number of pixels to be deducted
 * from the available height.
 * 
 * @module alfresco/layout/HeightMixin
 * @author Dave Draper
 * @since 1.0.34
 */
define(["dojo/_base/declare",
        "dojo/aspect",
        "dojo/_base/lang",
        "dojo/Deferred",
        "dojo/dom-style",
        "dojo/when",
        "dojo/window",
        "dijit/registry",
        "jquery",
        "jqueryui"], 
        function(declare, aspect, lang, Deferred, domStyle, when, win, registry, $) {
   
   return declare([], {
      
      /**
       * This property allows for the height being set to be compensated for a number of different factors
       * such as sticky footers or configured padding or margins that might increase the height of the widget
       * DOM element.
       * 
       * @instance
       * @type {number} 
       * @default
       */
      heightAdjustment: 0,
      
      /**
       * <p>This should be configured to indicate how the height of the widget should be calculated.
       *   <ul>
       *     <li>"AUTO" (the default) indicates that the height will be calculated to be the available space from the widgets position
       * to the bottom of the screen (minus any [heightAdjustment]{@link module:alfresco/layout/HeightMixin#heightAdjustment}).<li>
       *     <li>"DIALOG" indicates that the height should be taken from the available height of the dialog in which the widget
       * is displayed.</li>
       *     <li>Any negative number indicates that the "AUTO" height minus the supplied value will be used.</li>
       *     <li>Any positive number indicates a fixed height (in pixels) that should be used for the height</li>
       *   </ul>
       * </p>
       * 
       * @instance
       * @type {string|number}
       * @default
       */
      heightMode: "AUTO",

      /**
       * Calculates the height when [heightMode]{@link module:alfresco/layout/HeightMixin#heightMode} is "DIALOG".
       * 
       * @instance
       * @param {element} domNode The DOM element to calculate the height for.
       * @returns {number|promise} Either an actual height or a promise of the height
       * @since 1.0.36
       */
      calculateDialogHeight: function alfresco_layout_HeightMixin__calculateDialogHeight(domNode) {
         var calculatedHeight = $(domNode).parentsUntil(".alfresco-dialog-AlfDialog .dialog-body").last().innerHeight();
         if (!calculatedHeight)
         {
            // When using the DIALOG mode it is necessary to return a promise of the height because it won't 
            // be possible to know the height until the dialog is displayed.
            var containingDialog = registry.byNode($(domNode).parents(".alfresco-dialog-AlfDialog")[0]);
            if (containingDialog)
            {
               var heightAdjustment = this.heightAdjustment; // Required to compensate for lack of *this*
               calculatedHeight = new Deferred();
               this.own(aspect.after(containingDialog, "_onFocus", function() {
                  var dialogHeight = $(domNode).parentsUntil(".alfresco-dialog-AlfDialog .dialog-body").first().innerHeight();
                  calculatedHeight.resolve(dialogHeight - heightAdjustment);
               }, true));
            }
         }
         else
         {
            calculatedHeight -= this.heightAdjustment; // Deduct the adjustment value...
         }
         return calculatedHeight;
      },

      /**
       * Calculates the height of the supplied element based on the available space using the configured 
       * [heightMode]{@link module:alfresco/layout/HeightMixin#heightMode} setting.
       * 
       * @instance
       * @param {element} domNode The DOM element to calculate the height for.
       * @returns {number|promise} Either an actual height or a promise of the height (when using the "DIALOG" height mode)
       */
      calculateHeight: function alfresco_layout_HeightMixin__calculateHeight(domNode) {
         var calculatedHeight;
         if (domNode)
         {
            // Get the position of the DOM node and the available view port height...
            var winBox = win.getBox();
            
            // We're using JQuery here to get the offset as it has proved more reliable than either the Dojo margin box
            // or native browser offsetTop options...
            var offset = $(domNode).offset().top;
            var availableHeight = winBox.h - offset - this.heightAdjustment;
            var heightMode = this.heightMode;
            if (heightMode === "DIALOG")
            {
               calculatedHeight = this.calculateDialogHeight(domNode);
            }
            else if (heightMode === "PARENT")
            {
               calculatedHeight = this.calculateParentHeight(domNode, offset);
            }
            else if (!heightMode || heightMode === "AUTO" || heightMode === "auto")
            {
               calculatedHeight =  availableHeight;
            }
            else if (heightMode < 0)
            {
               // If the height mode is a number less than zero, then deduct that height from the available space.
               calculatedHeight = availableHeight + heightMode; // NOTE: Not a mistake, remember adding a negative number substracts! :)
            }
            else
            {
               calculatedHeight = heightMode;
            }
         }
         return calculatedHeight;
      },

      /**
       * Calculates the height when [heightMode]{@link module:alfresco/layout/HeightMixin#heightMode} is "PARENT".
       * 
       * @instance
       * @param {element} domNode The DOM element to calculate the height for.
       * @returns {number|promise} Either an actual height or a promise of the height
       * @since 1.0.36
       */
      calculateParentHeight: function alfresco_layout_HeightMixin__calculateParentHeight(domNode, offset) {
         var calculatedHeight;
         var _this = this;
         $(domNode).parents().each(function() {
            var style = $(this).attr("style");
            if (style && style.indexOf("height:") !== -1)
            {
               var parentOffset = offset - $(this).offset().top;
               calculatedHeight = $(this).height() - parentOffset - _this.heightAdjustment;
               return false;
            }
         });
         return calculatedHeight;
      },

      /**
       * This sets the height of the supplied DOM element with the height returned from a call to the
       * [calculateHeight]{@link module:alfresco/layout/HeightMixin#calculateHeight} function.
       * 
       * @instance
       * @param {element} domNode The DOM element to set the height for.
       */
      setHeight: function alfresco_layout_HeightMixin__setHeight(domNode) {
         var height = this.calculateHeight(domNode);
         if (height || height === 0)
         {
            when(height, lang.hitch(this, function(value) {
               if (!isNaN(value))
               {
                  value = value + "px";
               }
               domStyle.set(domNode, "height", value);
            }));
         }
      }
   });
});