text: 'use strict';
  
  var Axes = require('../../plots/cartesian/axes');
  var alignPeriod = require('../../plots/cartesian/align_period');
  var mergeArray = require('../../lib').mergeArray;
  var calcSelection = require('../scatter/calc_selection');
  var BADNUM = require('../../constants/numerical').BADNUM;
  
  function isAbsolute(a) {
      return (a === 'a' || a === 'absolute');
  }
  
  function isTotal(a) {
      return (a === 't' || a === 'total');
  }
  
  module.exports = function calc(gd, trace) {
      var xa = Axes.getFromId(gd, trace.xaxis || 'x');
      var ya = Axes.getFromId(gd, trace.yaxis || 'y');
      var size, pos, origPos, pObj, hasPeriod, pLetter;
  
      if(trace.orientation === 'h') {
          size = xa.makeCalcdata(trace, 'x');
          origPos = ya.makeCalcdata(trace, 'y');
          pObj = alignPeriod(trace, ya, 'y', origPos);
          hasPeriod = !!trace.yperiodalignment;
          pLetter = 'y';
      } else {
          size = ya.makeCalcdata(trace, 'y');
          origPos = xa.makeCalcdata(trace, 'x');
          pObj = alignPeriod(trace, xa, 'x', origPos);
          hasPeriod = !!trace.xperiodalignment;
          pLetter = 'x';
      }
      pos = pObj.vals;
  
      // create the "calculated data" to plot
      var serieslen = Math.min(pos.length, size.length);
      var cd = new Array(serieslen);
  
      // set position and size (as well as for waterfall total size)
      var previousSum = 0;
      var newSize;
      // trace-wide flags
      var hasTotals = false;
  
      for(var i = 0; i < serieslen; i++) {
          var amount = size[i] || 0;
  
          var connectToNext = false;
          if(size[i] !== BADNUM || isTotal(trace.measure[i]) || isAbsolute(trace.measure[i])) {
              if(i + 1 < serieslen && (size[i + 1] !== BADNUM || isTotal(trace.measure[i + 1]) || isAbsolute(trace.measure[i + 1]))) {
                  connectToNext = true;
              }
          }
  
          var cdi = cd[i] = {
              i: i,
              p: pos[i],
              s: amount,
              rawS: amount,
              cNext: connectToNext
          };
  
          if(isAbsolute(trace.measure[i])) {
              previousSum = cdi.s;
  
              cdi.isSum = true;
              cdi.dir = 'totals';
              cdi.s = previousSum;
          } else if(isTotal(trace.measure[i])) {
              cdi.isSum = true;
              cdi.dir = 'totals';
              cdi.s = previousSum;
          } else {
              // default: relative
              cdi.isSum = false;
              cdi.dir = cdi.rawS < 0 ? 'decreasing' : 'increasing';
              newSize = cdi.s;
              cdi.s = previousSum + newSize;
              previousSum += newSize;
          }
  
          if(cdi.dir === 'totals') {
              hasTotals = true;
          }
  
          if(hasPeriod) {
              cd[i].orig_p = origPos[i]; // used by hover
              cd[i][pLetter + 'End'] = pObj.ends[i];
              cd[i][pLetter + 'Start'] = pObj.starts[i];
          }
  
          if(trace.ids) {
              cdi.id = String(trace.ids[i]);
          }
  
          cdi.v = (trace.base || 0) + previousSum;
      }
  
      if(cd.length) cd[0].hasTotals = hasTotals;
  
      mergeArray(trace.text, cd, 'tx');
      mergeArray(trace.hovertext, cd, 'htx');
      calcSelection(cd, trace);
  
      return cd;
  };
  