| /* | 
 | * 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. | 
 | */ | 
 |  | 
 | // Only create one roam controller for each coordinate system. | 
 | // one roam controller might be refered by two inside data zoom | 
 | // components (for example, one for x and one for y). When user | 
 | // pan or zoom, only dispatch one action for those data zoom | 
 | // components. | 
 |  | 
 | import RoamController, { RoamType } from '../../component/helper/RoamController'; | 
 | import * as throttleUtil from '../../util/throttle'; | 
 | import { makeInner } from '../../util/model'; | 
 | import { Dictionary, ZRElementEvent } from '../../util/types'; | 
 | import ExtensionAPI from '../../core/ExtensionAPI'; | 
 | import InsideZoomModel from './InsideZoomModel'; | 
 | import { each, curry, Curry1, HashMap, createHashMap } from 'zrender/src/core/util'; | 
 | import { | 
 |     DataZoomPayloadBatchItem, collectReferCoordSysModelInfo, | 
 |     DataZoomCoordSysMainType, DataZoomReferCoordSysInfo | 
 | } from './helper'; | 
 | import GlobalModel from '../../model/Global'; | 
 | import { CoordinateSystemHostModel } from '../../coord/CoordinateSystem'; | 
 | import { DataZoomGetRangeHandlers } from './InsideZoomView'; | 
 | import { EChartsExtensionInstallRegisters } from '../../extension'; | 
 |  | 
 |  | 
 | interface DataZoomInfo { | 
 |     getRange: DataZoomGetRangeHandlers; | 
 |     model: InsideZoomModel; | 
 |     dzReferCoordSysInfo: DataZoomReferCoordSysInfo | 
 | } | 
 |  | 
 | interface CoordSysRecord { | 
 |     // key: dataZoom.uid | 
 |     dataZoomInfoMap: HashMap<DataZoomInfo, string>; | 
 |     model: CoordinateSystemHostModel, | 
 |     // count: number | 
 |     // coordId: string | 
 |     controller: RoamController; | 
 |     containsPoint: (e: ZRElementEvent, x: number, y: number) => boolean; | 
 |     dispatchAction: Curry1<typeof dispatchAction, ExtensionAPI>; | 
 | } | 
 |  | 
 |  | 
 | const inner = makeInner<{ | 
 |     // key: coordSysModel.uid | 
 |     coordSysRecordMap: HashMap<CoordSysRecord, string>; | 
 | }, ExtensionAPI>(); | 
 |  | 
 | export function setViewInfoToCoordSysRecord( | 
 |     api: ExtensionAPI, | 
 |     dataZoomModel: InsideZoomModel, | 
 |     getRange: DataZoomGetRangeHandlers | 
 | ): void { | 
 |     inner(api).coordSysRecordMap.each(function (coordSysRecord) { | 
 |         const dzInfo = coordSysRecord.dataZoomInfoMap.get(dataZoomModel.uid); | 
 |         if (dzInfo) { | 
 |             dzInfo.getRange = getRange; | 
 |         } | 
 |     }); | 
 | } | 
 |  | 
 | export function disposeCoordSysRecordIfNeeded(api: ExtensionAPI, dataZoomModel: InsideZoomModel) { | 
 |     const coordSysRecordMap = inner(api).coordSysRecordMap; | 
 |     const coordSysKeyArr = coordSysRecordMap.keys(); | 
 |     for (let i = 0; i < coordSysKeyArr.length; i++) { | 
 |         const coordSysKey = coordSysKeyArr[i]; | 
 |         const coordSysRecord = coordSysRecordMap.get(coordSysKey); | 
 |         const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap; | 
 |         if (dataZoomInfoMap) { | 
 |             const dzUid = dataZoomModel.uid; | 
 |             const dzInfo = dataZoomInfoMap.get(dzUid); | 
 |             if (dzInfo) { | 
 |                 dataZoomInfoMap.removeKey(dzUid); | 
 |                 if (!dataZoomInfoMap.keys().length) { | 
 |                     disposeCoordSysRecord(coordSysRecordMap, coordSysRecord); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | function disposeCoordSysRecord( | 
 |     coordSysRecordMap: HashMap<CoordSysRecord, string>, | 
 |     coordSysRecord: CoordSysRecord | 
 | ): void { | 
 |     if (coordSysRecord) { | 
 |         coordSysRecordMap.removeKey(coordSysRecord.model.uid); | 
 |         const controller = coordSysRecord.controller; | 
 |         controller && controller.dispose(); | 
 |     } | 
 | } | 
 |  | 
 | function createCoordSysRecord(api: ExtensionAPI, coordSysModel: CoordinateSystemHostModel): CoordSysRecord { | 
 |     // These init props will never change after record created. | 
 |     const coordSysRecord: CoordSysRecord = { | 
 |         model: coordSysModel, | 
 |         containsPoint: curry(containsPoint, coordSysModel), | 
 |         dispatchAction: curry(dispatchAction, api), | 
 |         dataZoomInfoMap: null, | 
 |         controller: null | 
 |     }; | 
 |  | 
 |     // Must not do anything depends on coordSysRecord outside the event handler here, | 
 |     // because coordSysRecord not completed yet. | 
 |     const controller = coordSysRecord.controller = new RoamController(api.getZr()); | 
 |  | 
 |     each(['pan', 'zoom', 'scrollMove'] as const, function (eventName) { | 
 |         controller.on(eventName, function (event) { | 
 |             const batch: DataZoomPayloadBatchItem[] = []; | 
 |  | 
 |             coordSysRecord.dataZoomInfoMap.each(function (dzInfo) { | 
 |                 // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove, | 
 |                 // moveOnMouseWheel, ...) enabled. | 
 |                 if (!event.isAvailableBehavior(dzInfo.model.option)) { | 
 |                     return; | 
 |                 } | 
 |  | 
 |                 const method = (dzInfo.getRange || {} as DataZoomGetRangeHandlers)[eventName]; | 
 |                 const range = method && method( | 
 |                     dzInfo.dzReferCoordSysInfo, | 
 |                     coordSysRecord.model.mainType as DataZoomCoordSysMainType, | 
 |                     coordSysRecord.controller, | 
 |                     event as any | 
 |                 ); | 
 |  | 
 |                 !dzInfo.model.get('disabled', true) && range && batch.push({ | 
 |                     dataZoomId: dzInfo.model.id, | 
 |                     start: range[0], | 
 |                     end: range[1] | 
 |                 }); | 
 |             }); | 
 |  | 
 |             batch.length && coordSysRecord.dispatchAction(batch); | 
 |         }); | 
 |     }); | 
 |  | 
 |     return coordSysRecord; | 
 | } | 
 |  | 
 | /** | 
 |  * This action will be throttled. | 
 |  */ | 
 | function dispatchAction(api: ExtensionAPI, batch: DataZoomPayloadBatchItem[]) { | 
 |     if (!api.isDisposed()) { | 
 |         api.dispatchAction({ | 
 |             type: 'dataZoom', | 
 |             animation: { | 
 |                 easing: 'cubicOut', | 
 |                 duration: 100 | 
 |             }, | 
 |             batch: batch | 
 |         }); | 
 |     } | 
 | } | 
 |  | 
 | function containsPoint( | 
 |     coordSysModel: CoordinateSystemHostModel, e: ZRElementEvent, x: number, y: number | 
 | ): boolean { | 
 |     return coordSysModel.coordinateSystem.containPoint([x, y]); | 
 | } | 
 |  | 
 | /** | 
 |  * Merge roamController settings when multiple dataZooms share one roamController. | 
 |  */ | 
 | function mergeControllerParams(dataZoomInfoMap: HashMap<{ model: InsideZoomModel }>) { | 
 |     let controlType: RoamType; | 
 |     // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated | 
 |     // as string, it is probably revert to reserved word by compress tool. See #7411. | 
 |     const prefix = 'type_'; | 
 |     const typePriority: Dictionary<number> = { | 
 |         'type_true': 2, | 
 |         'type_move': 1, | 
 |         'type_false': 0, | 
 |         'type_undefined': -1 | 
 |     }; | 
 |     let preventDefaultMouseMove = true; | 
 |  | 
 |     dataZoomInfoMap.each(function (dataZoomInfo) { | 
 |         const dataZoomModel = dataZoomInfo.model; | 
 |         const oneType = dataZoomModel.get('disabled', true) | 
 |             ? false | 
 |             : dataZoomModel.get('zoomLock', true) | 
 |             ? 'move' as const | 
 |             : true; | 
 |         if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) { | 
 |             controlType = oneType; | 
 |         } | 
 |  | 
 |         // Prevent default move event by default. If one false, do not prevent. Otherwise | 
 |         // users may be confused why it does not work when multiple insideZooms exist. | 
 |         preventDefaultMouseMove = preventDefaultMouseMove | 
 |             && dataZoomModel.get('preventDefaultMouseMove', true); | 
 |     }); | 
 |  | 
 |     return { | 
 |         controlType: controlType, | 
 |         opt: { | 
 |             // RoamController will enable all of these functionalities, | 
 |             // and the final behavior is determined by its event listener | 
 |             // provided by each inside zoom. | 
 |             zoomOnMouseWheel: true, | 
 |             moveOnMouseMove: true, | 
 |             moveOnMouseWheel: true, | 
 |             preventDefaultMouseMove: !!preventDefaultMouseMove | 
 |         } | 
 |     }; | 
 | } | 
 |  | 
 | export function installDataZoomRoamProcessor(registers: EChartsExtensionInstallRegisters) { | 
 |  | 
 |     registers.registerProcessor( | 
 |         registers.PRIORITY.PROCESSOR.FILTER, | 
 |         function (ecModel: GlobalModel, api: ExtensionAPI): void { | 
 |             const apiInner = inner(api); | 
 |             const coordSysRecordMap = apiInner.coordSysRecordMap | 
 |                 || (apiInner.coordSysRecordMap = createHashMap<CoordSysRecord, string>()); | 
 |  | 
 |             coordSysRecordMap.each(function (coordSysRecord) { | 
 |                 // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should | 
 |                 // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow. | 
 |                 coordSysRecord.dataZoomInfoMap = null; | 
 |             }); | 
 |  | 
 |             ecModel.eachComponent( | 
 |                 { mainType: 'dataZoom', subType: 'inside' }, | 
 |                 function (dataZoomModel: InsideZoomModel) { | 
 |                     const dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel); | 
 |  | 
 |                     each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) { | 
 |  | 
 |                         const coordSysUid = dzCoordSysInfo.model.uid; | 
 |                         const coordSysRecord = coordSysRecordMap.get(coordSysUid) | 
 |                             || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model)); | 
 |  | 
 |                         const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap | 
 |                             || (coordSysRecord.dataZoomInfoMap = createHashMap<DataZoomInfo, string>()); | 
 |                         // Notice these props might be changed each time for a single dataZoomModel. | 
 |                         dataZoomInfoMap.set(dataZoomModel.uid, { | 
 |                             dzReferCoordSysInfo: dzCoordSysInfo, | 
 |                             model: dataZoomModel, | 
 |                             getRange: null | 
 |                         }); | 
 |                     }); | 
 |                 } | 
 |             ); | 
 |  | 
 |             // (1) Merge dataZoom settings for each coord sys and set to the roam controller. | 
 |             // (2) Clear coord sys if not refered by any dataZoom. | 
 |             coordSysRecordMap.each(function (coordSysRecord) { | 
 |                 const controller = coordSysRecord.controller; | 
 |                 let firstDzInfo: DataZoomInfo; | 
 |                 const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap; | 
 |  | 
 |                 if (dataZoomInfoMap) { | 
 |                     const firstDzKey = dataZoomInfoMap.keys()[0]; | 
 |                     if (firstDzKey != null) { | 
 |                         firstDzInfo = dataZoomInfoMap.get(firstDzKey); | 
 |                     } | 
 |                 } | 
 |  | 
 |                 if (!firstDzInfo) { | 
 |                     disposeCoordSysRecord(coordSysRecordMap, coordSysRecord); | 
 |                     return; | 
 |                 } | 
 |  | 
 |                 const controllerParams = mergeControllerParams(dataZoomInfoMap); | 
 |                 controller.enable(controllerParams.controlType, controllerParams.opt); | 
 |  | 
 |                 controller.setPointerChecker(coordSysRecord.containsPoint); | 
 |  | 
 |                 throttleUtil.createOrUpdate( | 
 |                     coordSysRecord, | 
 |                     'dispatchAction', | 
 |                     firstDzInfo.model.get('throttle', true), | 
 |                     'fixRate' | 
 |                 ); | 
 |             }); | 
 |     }); | 
 |  | 
 | } |