| 
/* 
 | 
* Licensed to the Apache Software Foundation (ASF) under one 
 | 
* or more contributor license agreements.  See the NOTICE file 
 | 
* distributed with this work for additional information 
 | 
* regarding copyright ownership.  The ASF licenses this file 
 | 
* to you 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. 
 | 
*/ 
 | 
  
 | 
  
 | 
/** 
 | 
 * AUTO-GENERATED FILE. DO NOT MODIFY. 
 | 
 */ 
 | 
  
 | 
/* 
 | 
* Licensed to the Apache Software Foundation (ASF) under one 
 | 
* or more contributor license agreements.  See the NOTICE file 
 | 
* distributed with this work for additional information 
 | 
* regarding copyright ownership.  The ASF licenses this file 
 | 
* to you 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. 
 | 
*/ 
 | 
// Layout helpers for each component positioning 
 | 
import * as zrUtil from 'zrender/lib/core/util.js'; 
 | 
import BoundingRect from 'zrender/lib/core/BoundingRect.js'; 
 | 
import { parsePercent } from './number.js'; 
 | 
import * as formatUtil from './format.js'; 
 | 
var each = zrUtil.each; 
 | 
/** 
 | 
 * @public 
 | 
 */ 
 | 
export var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height']; 
 | 
/** 
 | 
 * @public 
 | 
 */ 
 | 
export var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']]; 
 | 
function boxLayout(orient, group, gap, maxWidth, maxHeight) { 
 | 
  var x = 0; 
 | 
  var y = 0; 
 | 
  if (maxWidth == null) { 
 | 
    maxWidth = Infinity; 
 | 
  } 
 | 
  if (maxHeight == null) { 
 | 
    maxHeight = Infinity; 
 | 
  } 
 | 
  var currentLineMaxSize = 0; 
 | 
  group.eachChild(function (child, idx) { 
 | 
    var rect = child.getBoundingRect(); 
 | 
    var nextChild = group.childAt(idx + 1); 
 | 
    var nextChildRect = nextChild && nextChild.getBoundingRect(); 
 | 
    var nextX; 
 | 
    var nextY; 
 | 
    if (orient === 'horizontal') { 
 | 
      var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0); 
 | 
      nextX = x + moveX; 
 | 
      // Wrap when width exceeds maxWidth or meet a `newline` group 
 | 
      // FIXME compare before adding gap? 
 | 
      if (nextX > maxWidth || child.newline) { 
 | 
        x = 0; 
 | 
        nextX = moveX; 
 | 
        y += currentLineMaxSize + gap; 
 | 
        currentLineMaxSize = rect.height; 
 | 
      } else { 
 | 
        // FIXME: consider rect.y is not `0`? 
 | 
        currentLineMaxSize = Math.max(currentLineMaxSize, rect.height); 
 | 
      } 
 | 
    } else { 
 | 
      var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0); 
 | 
      nextY = y + moveY; 
 | 
      // Wrap when width exceeds maxHeight or meet a `newline` group 
 | 
      if (nextY > maxHeight || child.newline) { 
 | 
        x += currentLineMaxSize + gap; 
 | 
        y = 0; 
 | 
        nextY = moveY; 
 | 
        currentLineMaxSize = rect.width; 
 | 
      } else { 
 | 
        currentLineMaxSize = Math.max(currentLineMaxSize, rect.width); 
 | 
      } 
 | 
    } 
 | 
    if (child.newline) { 
 | 
      return; 
 | 
    } 
 | 
    child.x = x; 
 | 
    child.y = y; 
 | 
    child.markRedraw(); 
 | 
    orient === 'horizontal' ? x = nextX + gap : y = nextY + gap; 
 | 
  }); 
 | 
} 
 | 
/** 
 | 
 * VBox or HBox layouting 
 | 
 * @param {string} orient 
 | 
 * @param {module:zrender/graphic/Group} group 
 | 
 * @param {number} gap 
 | 
 * @param {number} [width=Infinity] 
 | 
 * @param {number} [height=Infinity] 
 | 
 */ 
 | 
export var box = boxLayout; 
 | 
/** 
 | 
 * VBox layouting 
 | 
 * @param {module:zrender/graphic/Group} group 
 | 
 * @param {number} gap 
 | 
 * @param {number} [width=Infinity] 
 | 
 * @param {number} [height=Infinity] 
 | 
 */ 
 | 
export var vbox = zrUtil.curry(boxLayout, 'vertical'); 
 | 
/** 
 | 
 * HBox layouting 
 | 
 * @param {module:zrender/graphic/Group} group 
 | 
 * @param {number} gap 
 | 
 * @param {number} [width=Infinity] 
 | 
 * @param {number} [height=Infinity] 
 | 
 */ 
 | 
export var hbox = zrUtil.curry(boxLayout, 'horizontal'); 
 | 
/** 
 | 
 * If x or x2 is not specified or 'center' 'left' 'right', 
 | 
 * the width would be as long as possible. 
 | 
 * If y or y2 is not specified or 'middle' 'top' 'bottom', 
 | 
 * the height would be as long as possible. 
 | 
 */ 
 | 
export function getAvailableSize(positionInfo, containerRect, margin) { 
 | 
  var containerWidth = containerRect.width; 
 | 
  var containerHeight = containerRect.height; 
 | 
  var x = parsePercent(positionInfo.left, containerWidth); 
 | 
  var y = parsePercent(positionInfo.top, containerHeight); 
 | 
  var x2 = parsePercent(positionInfo.right, containerWidth); 
 | 
  var y2 = parsePercent(positionInfo.bottom, containerHeight); 
 | 
  (isNaN(x) || isNaN(parseFloat(positionInfo.left))) && (x = 0); 
 | 
  (isNaN(x2) || isNaN(parseFloat(positionInfo.right))) && (x2 = containerWidth); 
 | 
  (isNaN(y) || isNaN(parseFloat(positionInfo.top))) && (y = 0); 
 | 
  (isNaN(y2) || isNaN(parseFloat(positionInfo.bottom))) && (y2 = containerHeight); 
 | 
  margin = formatUtil.normalizeCssArray(margin || 0); 
 | 
  return { 
 | 
    width: Math.max(x2 - x - margin[1] - margin[3], 0), 
 | 
    height: Math.max(y2 - y - margin[0] - margin[2], 0) 
 | 
  }; 
 | 
} 
 | 
/** 
 | 
 * Parse position info. 
 | 
 */ 
 | 
export function getLayoutRect(positionInfo, containerRect, margin) { 
 | 
  margin = formatUtil.normalizeCssArray(margin || 0); 
 | 
  var containerWidth = containerRect.width; 
 | 
  var containerHeight = containerRect.height; 
 | 
  var left = parsePercent(positionInfo.left, containerWidth); 
 | 
  var top = parsePercent(positionInfo.top, containerHeight); 
 | 
  var right = parsePercent(positionInfo.right, containerWidth); 
 | 
  var bottom = parsePercent(positionInfo.bottom, containerHeight); 
 | 
  var width = parsePercent(positionInfo.width, containerWidth); 
 | 
  var height = parsePercent(positionInfo.height, containerHeight); 
 | 
  var verticalMargin = margin[2] + margin[0]; 
 | 
  var horizontalMargin = margin[1] + margin[3]; 
 | 
  var aspect = positionInfo.aspect; 
 | 
  // If width is not specified, calculate width from left and right 
 | 
  if (isNaN(width)) { 
 | 
    width = containerWidth - right - horizontalMargin - left; 
 | 
  } 
 | 
  if (isNaN(height)) { 
 | 
    height = containerHeight - bottom - verticalMargin - top; 
 | 
  } 
 | 
  if (aspect != null) { 
 | 
    // If width and height are not given 
 | 
    // 1. Graph should not exceeds the container 
 | 
    // 2. Aspect must be keeped 
 | 
    // 3. Graph should take the space as more as possible 
 | 
    // FIXME 
 | 
    // Margin is not considered, because there is no case that both 
 | 
    // using margin and aspect so far. 
 | 
    if (isNaN(width) && isNaN(height)) { 
 | 
      if (aspect > containerWidth / containerHeight) { 
 | 
        width = containerWidth * 0.8; 
 | 
      } else { 
 | 
        height = containerHeight * 0.8; 
 | 
      } 
 | 
    } 
 | 
    // Calculate width or height with given aspect 
 | 
    if (isNaN(width)) { 
 | 
      width = aspect * height; 
 | 
    } 
 | 
    if (isNaN(height)) { 
 | 
      height = width / aspect; 
 | 
    } 
 | 
  } 
 | 
  // If left is not specified, calculate left from right and width 
 | 
  if (isNaN(left)) { 
 | 
    left = containerWidth - right - width - horizontalMargin; 
 | 
  } 
 | 
  if (isNaN(top)) { 
 | 
    top = containerHeight - bottom - height - verticalMargin; 
 | 
  } 
 | 
  // Align left and top 
 | 
  switch (positionInfo.left || positionInfo.right) { 
 | 
    case 'center': 
 | 
      left = containerWidth / 2 - width / 2 - margin[3]; 
 | 
      break; 
 | 
    case 'right': 
 | 
      left = containerWidth - width - horizontalMargin; 
 | 
      break; 
 | 
  } 
 | 
  switch (positionInfo.top || positionInfo.bottom) { 
 | 
    case 'middle': 
 | 
    case 'center': 
 | 
      top = containerHeight / 2 - height / 2 - margin[0]; 
 | 
      break; 
 | 
    case 'bottom': 
 | 
      top = containerHeight - height - verticalMargin; 
 | 
      break; 
 | 
  } 
 | 
  // If something is wrong and left, top, width, height are calculated as NaN 
 | 
  left = left || 0; 
 | 
  top = top || 0; 
 | 
  if (isNaN(width)) { 
 | 
    // Width may be NaN if only one value is given except width 
 | 
    width = containerWidth - horizontalMargin - left - (right || 0); 
 | 
  } 
 | 
  if (isNaN(height)) { 
 | 
    // Height may be NaN if only one value is given except height 
 | 
    height = containerHeight - verticalMargin - top - (bottom || 0); 
 | 
  } 
 | 
  var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); 
 | 
  rect.margin = margin; 
 | 
  return rect; 
 | 
} 
 | 
/** 
 | 
 * Position a zr element in viewport 
 | 
 *  Group position is specified by either 
 | 
 *  {left, top}, {right, bottom} 
 | 
 *  If all properties exists, right and bottom will be igonred. 
 | 
 * 
 | 
 * Logic: 
 | 
 *     1. Scale (against origin point in parent coord) 
 | 
 *     2. Rotate (against origin point in parent coord) 
 | 
 *     3. Translate (with el.position by this method) 
 | 
 * So this method only fixes the last step 'Translate', which does not affect 
 | 
 * scaling and rotating. 
 | 
 * 
 | 
 * If be called repeatedly with the same input el, the same result will be gotten. 
 | 
 * 
 | 
 * Return true if the layout happened. 
 | 
 * 
 | 
 * @param el Should have `getBoundingRect` method. 
 | 
 * @param positionInfo 
 | 
 * @param positionInfo.left 
 | 
 * @param positionInfo.top 
 | 
 * @param positionInfo.right 
 | 
 * @param positionInfo.bottom 
 | 
 * @param positionInfo.width Only for opt.boundingModel: 'raw' 
 | 
 * @param positionInfo.height Only for opt.boundingModel: 'raw' 
 | 
 * @param containerRect 
 | 
 * @param margin 
 | 
 * @param opt 
 | 
 * @param opt.hv Only horizontal or only vertical. Default to be [1, 1] 
 | 
 * @param opt.boundingMode 
 | 
 *        Specify how to calculate boundingRect when locating. 
 | 
 *        'all': Position the boundingRect that is transformed and uioned 
 | 
 *               both itself and its descendants. 
 | 
 *               This mode simplies confine the elements in the bounding 
 | 
 *               of their container (e.g., using 'right: 0'). 
 | 
 *        'raw': Position the boundingRect that is not transformed and only itself. 
 | 
 *               This mode is useful when you want a element can overflow its 
 | 
 *               container. (Consider a rotated circle needs to be located in a corner.) 
 | 
 *               In this mode positionInfo.width/height can only be number. 
 | 
 */ 
 | 
export function positionElement(el, positionInfo, containerRect, margin, opt, out) { 
 | 
  var h = !opt || !opt.hv || opt.hv[0]; 
 | 
  var v = !opt || !opt.hv || opt.hv[1]; 
 | 
  var boundingMode = opt && opt.boundingMode || 'all'; 
 | 
  out = out || el; 
 | 
  out.x = el.x; 
 | 
  out.y = el.y; 
 | 
  if (!h && !v) { 
 | 
    return false; 
 | 
  } 
 | 
  var rect; 
 | 
  if (boundingMode === 'raw') { 
 | 
    rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect(); 
 | 
  } else { 
 | 
    rect = el.getBoundingRect(); 
 | 
    if (el.needLocalTransform()) { 
 | 
      var transform = el.getLocalTransform(); 
 | 
      // Notice: raw rect may be inner object of el, 
 | 
      // which should not be modified. 
 | 
      rect = rect.clone(); 
 | 
      rect.applyTransform(transform); 
 | 
    } 
 | 
  } 
 | 
  // The real width and height can not be specified but calculated by the given el. 
 | 
  var layoutRect = getLayoutRect(zrUtil.defaults({ 
 | 
    width: rect.width, 
 | 
    height: rect.height 
 | 
  }, positionInfo), containerRect, margin); 
 | 
  // Because 'tranlate' is the last step in transform 
 | 
  // (see zrender/core/Transformable#getLocalTransform), 
 | 
  // we can just only modify el.position to get final result. 
 | 
  var dx = h ? layoutRect.x - rect.x : 0; 
 | 
  var dy = v ? layoutRect.y - rect.y : 0; 
 | 
  if (boundingMode === 'raw') { 
 | 
    out.x = dx; 
 | 
    out.y = dy; 
 | 
  } else { 
 | 
    out.x += dx; 
 | 
    out.y += dy; 
 | 
  } 
 | 
  if (out === el) { 
 | 
    el.markRedraw(); 
 | 
  } 
 | 
  return true; 
 | 
} 
 | 
/** 
 | 
 * @param option Contains some of the properties in HV_NAMES. 
 | 
 * @param hvIdx 0: horizontal; 1: vertical. 
 | 
 */ 
 | 
export function sizeCalculable(option, hvIdx) { 
 | 
  return option[HV_NAMES[hvIdx][0]] != null || option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null; 
 | 
} 
 | 
export function fetchLayoutMode(ins) { 
 | 
  var layoutMode = ins.layoutMode || ins.constructor.layoutMode; 
 | 
  return zrUtil.isObject(layoutMode) ? layoutMode : layoutMode ? { 
 | 
    type: layoutMode 
 | 
  } : null; 
 | 
} 
 | 
/** 
 | 
 * Consider Case: 
 | 
 * When default option has {left: 0, width: 100}, and we set {right: 0} 
 | 
 * through setOption or media query, using normal zrUtil.merge will cause 
 | 
 * {right: 0} does not take effect. 
 | 
 * 
 | 
 * @example 
 | 
 * ComponentModel.extend({ 
 | 
 *     init: function () { 
 | 
 *         ... 
 | 
 *         let inputPositionParams = layout.getLayoutParams(option); 
 | 
 *         this.mergeOption(inputPositionParams); 
 | 
 *     }, 
 | 
 *     mergeOption: function (newOption) { 
 | 
 *         newOption && zrUtil.merge(thisOption, newOption, true); 
 | 
 *         layout.mergeLayoutParam(thisOption, newOption); 
 | 
 *     } 
 | 
 * }); 
 | 
 * 
 | 
 * @param targetOption 
 | 
 * @param newOption 
 | 
 * @param opt 
 | 
 */ 
 | 
export function mergeLayoutParam(targetOption, newOption, opt) { 
 | 
  var ignoreSize = opt && opt.ignoreSize; 
 | 
  !zrUtil.isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]); 
 | 
  var hResult = merge(HV_NAMES[0], 0); 
 | 
  var vResult = merge(HV_NAMES[1], 1); 
 | 
  copy(HV_NAMES[0], targetOption, hResult); 
 | 
  copy(HV_NAMES[1], targetOption, vResult); 
 | 
  function merge(names, hvIdx) { 
 | 
    var newParams = {}; 
 | 
    var newValueCount = 0; 
 | 
    var merged = {}; 
 | 
    var mergedValueCount = 0; 
 | 
    var enoughParamNumber = 2; 
 | 
    each(names, function (name) { 
 | 
      merged[name] = targetOption[name]; 
 | 
    }); 
 | 
    each(names, function (name) { 
 | 
      // Consider case: newOption.width is null, which is 
 | 
      // set by user for removing width setting. 
 | 
      hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]); 
 | 
      hasValue(newParams, name) && newValueCount++; 
 | 
      hasValue(merged, name) && mergedValueCount++; 
 | 
    }); 
 | 
    if (ignoreSize[hvIdx]) { 
 | 
      // Only one of left/right is premitted to exist. 
 | 
      if (hasValue(newOption, names[1])) { 
 | 
        merged[names[2]] = null; 
 | 
      } else if (hasValue(newOption, names[2])) { 
 | 
        merged[names[1]] = null; 
 | 
      } 
 | 
      return merged; 
 | 
    } 
 | 
    // Case: newOption: {width: ..., right: ...}, 
 | 
    // or targetOption: {right: ...} and newOption: {width: ...}, 
 | 
    // There is no conflict when merged only has params count 
 | 
    // little than enoughParamNumber. 
 | 
    if (mergedValueCount === enoughParamNumber || !newValueCount) { 
 | 
      return merged; 
 | 
    } 
 | 
    // Case: newOption: {width: ..., right: ...}, 
 | 
    // Than we can make sure user only want those two, and ignore 
 | 
    // all origin params in targetOption. 
 | 
    else if (newValueCount >= enoughParamNumber) { 
 | 
      return newParams; 
 | 
    } else { 
 | 
      // Chose another param from targetOption by priority. 
 | 
      for (var i = 0; i < names.length; i++) { 
 | 
        var name_1 = names[i]; 
 | 
        if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) { 
 | 
          newParams[name_1] = targetOption[name_1]; 
 | 
          break; 
 | 
        } 
 | 
      } 
 | 
      return newParams; 
 | 
    } 
 | 
  } 
 | 
  function hasProp(obj, name) { 
 | 
    return obj.hasOwnProperty(name); 
 | 
  } 
 | 
  function hasValue(obj, name) { 
 | 
    return obj[name] != null && obj[name] !== 'auto'; 
 | 
  } 
 | 
  function copy(names, target, source) { 
 | 
    each(names, function (name) { 
 | 
      target[name] = source[name]; 
 | 
    }); 
 | 
  } 
 | 
} 
 | 
/** 
 | 
 * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. 
 | 
 */ 
 | 
export function getLayoutParams(source) { 
 | 
  return copyLayoutParams({}, source); 
 | 
} 
 | 
/** 
 | 
 * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. 
 | 
 * @param {Object} source 
 | 
 * @return {Object} Result contains those props. 
 | 
 */ 
 | 
export function copyLayoutParams(target, source) { 
 | 
  source && target && each(LOCATION_PARAMS, function (name) { 
 | 
    source.hasOwnProperty(name) && (target[name] = source[name]); 
 | 
  }); 
 | 
  return target; 
 | 
} 
 |