| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 | 
/** 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.*/var zrUtil = require("zrender/lib/core/util");var _model = require("../../util/model");var makeInner = _model.makeInner;var modelHelper = require("./modelHelper");var findPointFromSeries = require("./findPointFromSeries");/** 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.*/var each = zrUtil.each;var curry = zrUtil.curry;var inner = makeInner();/** * Basic logic: check all axis, if they do not demand show/highlight, * then hide/downplay them. * * @param {Object} coordSysAxesInfo * @param {Object} payload * @param {string} [payload.currTrigger] 'click' | 'mousemove' | 'leave' * @param {Array.<number>} [payload.x] x and y, which are mandatory, specify a point to *              trigger axisPointer and tooltip. * @param {Array.<number>} [payload.y] x and y, which are mandatory, specify a point to *              trigger axisPointer and tooltip. * @param {Object} [payload.seriesIndex] finder, optional, restrict target axes. * @param {Object} [payload.dataIndex] finder, restrict target axes. * @param {Object} [payload.axesInfo] finder, restrict target axes. *        [{ *          axisDim: 'x'|'y'|'angle'|..., *          axisIndex: ..., *          value: ... *        }, ...] * @param {Function} [payload.dispatchAction] * @param {Object} [payload.tooltipOption] * @param {Object|Array.<number>|Function} [payload.position] Tooltip position, *        which can be specified in dispatchAction * @param {module:echarts/model/Global} ecModel * @param {module:echarts/ExtensionAPI} api * @return {Object} content of event obj for echarts.connect. */function _default(payload, ecModel, api) {  var currTrigger = payload.currTrigger;  var point = [payload.x, payload.y];  var finder = payload;  var dispatchAction = payload.dispatchAction || zrUtil.bind(api.dispatchAction, api);  var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending  // See #6121. But we are not able to reproduce it yet.  if (!coordSysAxesInfo) {    return;  }  if (illegalPoint(point)) {    // Used in the default behavior of `connection`: use the sample seriesIndex    // and dataIndex. And also used in the tooltipView trigger.    point = findPointFromSeries({      seriesIndex: finder.seriesIndex,      // Do not use dataIndexInside from other ec instance.      // FIXME: auto detect it?      dataIndex: finder.dataIndex    }, ecModel).point;  }  var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).  // Notice: In this case, it is difficult to get the `point` (which is necessary to show  // tooltip, so if point is not given, we just use the point found by sample seriesIndex  // and dataIndex.  var inputAxesInfo = finder.axesInfo;  var axesInfo = coordSysAxesInfo.axesInfo;  var shouldHide = currTrigger === 'leave' || illegalPoint(point);  var outputFinder = {};  var showValueMap = {};  var dataByCoordSys = {    list: [],    map: {}  };  var updaters = {    showPointer: curry(showPointer, showValueMap),    showTooltip: curry(showTooltip, dataByCoordSys)  }; // Process for triggered axes.  each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {    // If a point given, it must be contained by the coordinate system.    var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);    each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {      var axis = axisInfo.axis;      var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.      if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {        var val = inputAxisInfo && inputAxisInfo.value;        if (val == null && !isIllegalPoint) {          val = axis.pointToData(point);        }        val != null && processOnAxis(axisInfo, val, updaters, false, outputFinder);      }    });  }); // Process for linked axes.  var linkTriggers = {};  each(axesInfo, function (tarAxisInfo, tarKey) {    var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.    if (linkGroup && !showValueMap[tarKey]) {      each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {        var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.        if (srcAxisInfo !== tarAxisInfo && srcValItem) {          var val = srcValItem.value;          linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));          linkTriggers[tarAxisInfo.key] = val;        }      });    }  });  each(linkTriggers, function (val, tarKey) {    processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder);  });  updateModelActually(showValueMap, axesInfo, outputFinder);  dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);  dispatchHighDownActually(axesInfo, dispatchAction, api);  return outputFinder;}function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) {  var axis = axisInfo.axis;  if (axis.scale.isBlank() || !axis.containData(newValue)) {    return;  }  if (!axisInfo.involveSeries) {    updaters.showPointer(axisInfo, newValue);    return;  } // Heavy calculation. So put it after axis.containData checking.  var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);  var payloadBatch = payloadInfo.payloadBatch;  var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.  // By default use the first involved series data as a sample to connect.  if (payloadBatch[0] && outputFinder.seriesIndex == null) {    zrUtil.extend(outputFinder, payloadBatch[0]);  } // If no linkSource input, this process is for collecting link  // target, where snap should not be accepted.  if (!dontSnap && axisInfo.snap) {    if (axis.containData(snapToValue) && snapToValue != null) {      newValue = snapToValue;    }  }  updaters.showPointer(axisInfo, newValue, payloadBatch, outputFinder); // Tooltip should always be snapToValue, otherwise there will be  // incorrect "axis value ~ series value" mapping displayed in tooltip.  updaters.showTooltip(axisInfo, payloadInfo, snapToValue);}function buildPayloadsBySeries(value, axisInfo) {  var axis = axisInfo.axis;  var dim = axis.dim;  var snapToValue = value;  var payloadBatch = [];  var minDist = Number.MAX_VALUE;  var minDiff = -1;  each(axisInfo.seriesModels, function (series, idx) {    var dataDim = series.getData().mapDimension(dim, true);    var seriesNestestValue;    var dataIndices;    if (series.getAxisTooltipData) {      var result = series.getAxisTooltipData(dataDim, value, axis);      dataIndices = result.dataIndices;      seriesNestestValue = result.nestestValue;    } else {      dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex      // when data length is not same.      // false,      axis.type === 'category' ? 0.5 : null);      if (!dataIndices.length) {        return;      }      seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);    }    if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {      return;    }    var diff = value - seriesNestestValue;    var dist = Math.abs(diff); // Consider category case    if (dist <= minDist) {      if (dist < minDist || diff >= 0 && minDiff < 0) {        minDist = dist;        minDiff = diff;        snapToValue = seriesNestestValue;        payloadBatch.length = 0;      }      each(dataIndices, function (dataIndex) {        payloadBatch.push({          seriesIndex: series.seriesIndex,          dataIndexInside: dataIndex,          dataIndex: series.getData().getRawIndex(dataIndex)        });      });    }  });  return {    payloadBatch: payloadBatch,    snapToValue: snapToValue  };}function showPointer(showValueMap, axisInfo, value, payloadBatch) {  showValueMap[axisInfo.key] = {    value: value,    payloadBatch: payloadBatch  };}function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {  var payloadBatch = payloadInfo.payloadBatch;  var axis = axisInfo.axis;  var axisModel = axis.model;  var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,  // whose length will be used to judge whether dispatch action.  if (!axisInfo.triggerTooltip || !payloadBatch.length) {    return;  }  var coordSysModel = axisInfo.coordSys.model;  var coordSysKey = modelHelper.makeKey(coordSysModel);  var coordSysItem = dataByCoordSys.map[coordSysKey];  if (!coordSysItem) {    coordSysItem = dataByCoordSys.map[coordSysKey] = {      coordSysId: coordSysModel.id,      coordSysIndex: coordSysModel.componentIndex,      coordSysType: coordSysModel.type,      coordSysMainType: coordSysModel.mainType,      dataByAxis: []    };    dataByCoordSys.list.push(coordSysItem);  }  coordSysItem.dataByAxis.push({    axisDim: axis.dim,    axisIndex: axisModel.componentIndex,    axisType: axisModel.type,    axisId: axisModel.id,    value: value,    // Caustion: viewHelper.getValueLabel is actually on "view stage", which    // depends that all models have been updated. So it should not be performed    // here. Considering axisPointerModel used here is volatile, which is hard    // to be retrieve in TooltipView, we prepare parameters here.    valueLabelOpt: {      precision: axisPointerModel.get('label.precision'),      formatter: axisPointerModel.get('label.formatter')    },    seriesDataIndices: payloadBatch.slice()  });}function updateModelActually(showValueMap, axesInfo, outputFinder) {  var outputAxesInfo = outputFinder.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.  each(axesInfo, function (axisInfo, key) {    var option = axisInfo.axisPointerModel.option;    var valItem = showValueMap[key];    if (valItem) {      !axisInfo.useHandle && (option.status = 'show');      option.value = valItem.value; // For label formatter param and highlight.      option.seriesDataIndices = (valItem.payloadBatch || []).slice();    } // When always show (e.g., handle used), remain    // original value and status.    else {        // If hide, value still need to be set, consider        // click legend to toggle axis blank.        !axisInfo.useHandle && (option.status = 'hide');      } // If status is 'hide', should be no info in payload.    option.status === 'show' && outputAxesInfo.push({      axisDim: axisInfo.axis.dim,      axisIndex: axisInfo.axis.model.componentIndex,      value: option.value    });  });}function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {  // Basic logic: If no showTip required, hideTip will be dispatched.  if (illegalPoint(point) || !dataByCoordSys.list.length) {    dispatchAction({      type: 'hideTip'    });    return;  } // In most case only one axis (or event one series is used). It is  // convinient to fetch payload.seriesIndex and payload.dataIndex  // dirtectly. So put the first seriesIndex and dataIndex of the first  // axis on the payload.  var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};  dispatchAction({    type: 'showTip',    escapeConnect: true,    x: point[0],    y: point[1],    tooltipOption: payload.tooltipOption,    position: payload.position,    dataIndexInside: sampleItem.dataIndexInside,    dataIndex: sampleItem.dataIndex,    seriesIndex: sampleItem.seriesIndex,    dataByCoordSys: dataByCoordSys.list  });}function dispatchHighDownActually(axesInfo, dispatchAction, api) {  // FIXME  // highlight status modification shoule be a stage of main process?  // (Consider confilct (e.g., legend and axisPointer) and setOption)  var zr = api.getZr();  var highDownKey = 'axisPointerLastHighlights';  var lastHighlights = inner(zr)[highDownKey] || {};  var newHighlights = inner(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.  // Build hash map and remove duplicate incidentally.  each(axesInfo, function (axisInfo, key) {    var option = axisInfo.axisPointerModel.option;    option.status === 'show' && each(option.seriesDataIndices, function (batchItem) {      var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;      newHighlights[key] = batchItem;    });  }); // Diff.  var toHighlight = [];  var toDownplay = [];  zrUtil.each(lastHighlights, function (batchItem, key) {    !newHighlights[key] && toDownplay.push(batchItem);  });  zrUtil.each(newHighlights, function (batchItem, key) {    !lastHighlights[key] && toHighlight.push(batchItem);  });  toDownplay.length && api.dispatchAction({    type: 'downplay',    escapeConnect: true,    batch: toDownplay  });  toHighlight.length && api.dispatchAction({    type: 'highlight',    escapeConnect: true,    batch: toHighlight  });}function findInputAxisInfo(inputAxesInfo, axisInfo) {  for (var i = 0; i < (inputAxesInfo || []).length; i++) {    var inputAxisInfo = inputAxesInfo[i];    if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {      return inputAxisInfo;    }  }}function makeMapperParam(axisInfo) {  var axisModel = axisInfo.axis.model;  var item = {};  var dim = item.axisDim = axisInfo.axis.dim;  item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;  item.axisName = item[dim + 'AxisName'] = axisModel.name;  item.axisId = item[dim + 'AxisId'] = axisModel.id;  return item;}function illegalPoint(point) {  return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);}module.exports = _default;
 |