|  | /* | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @file Visual solution, for consistent option specification. | 
|  | */ | 
|  |  | 
|  | import * as zrUtil from 'zrender/src/core/util'; | 
|  | import VisualMapping, { VisualMappingOption } from './VisualMapping'; | 
|  | import { Dictionary } from 'zrender/src/core/types'; | 
|  | import { | 
|  | BuiltinVisualProperty, | 
|  | ParsedValue, | 
|  | DimensionLoose, | 
|  | StageHandlerProgressExecutor, | 
|  | DimensionIndex | 
|  | } from '../util/types'; | 
|  | import SeriesData from '../data/SeriesData'; | 
|  | import { getItemVisualFromData, setItemVisualFromData } from './helper'; | 
|  |  | 
|  | const each = zrUtil.each; | 
|  |  | 
|  | type VisualMappingCollection<VisualState extends string> | 
|  | = { | 
|  | [key in VisualState]?: { | 
|  | [key in BuiltinVisualProperty]?: VisualMapping | 
|  | } & { | 
|  | __alphaForOpacity?: VisualMapping | 
|  | } | 
|  | }; | 
|  |  | 
|  | function hasKeys(obj: Dictionary<any>) { | 
|  | if (obj) { | 
|  | for (const name in obj) { | 
|  | if (obj.hasOwnProperty(name)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | type VisualOption = {[key in BuiltinVisualProperty]?: any}; | 
|  |  | 
|  |  | 
|  | export function createVisualMappings<VisualState extends string>( | 
|  | option: Partial<Record<VisualState, VisualOption>>, | 
|  | stateList: readonly VisualState[], | 
|  | supplementVisualOption: (mappingOption: VisualMappingOption, state: string) => void | 
|  | ) { | 
|  | const visualMappings: VisualMappingCollection<VisualState> = {}; | 
|  |  | 
|  | each(stateList, function (state) { | 
|  | const mappings = visualMappings[state] = createMappings(); | 
|  |  | 
|  | each(option[state], function (visualData: VisualOption, visualType: BuiltinVisualProperty) { | 
|  | if (!VisualMapping.isValidType(visualType)) { | 
|  | return; | 
|  | } | 
|  | let mappingOption = { | 
|  | type: visualType, | 
|  | visual: visualData | 
|  | }; | 
|  | supplementVisualOption && supplementVisualOption(mappingOption, state); | 
|  | mappings[visualType] = new VisualMapping(mappingOption); | 
|  |  | 
|  | // Prepare a alpha for opacity, for some case that opacity | 
|  | // is not supported, such as rendering using gradient color. | 
|  | if (visualType === 'opacity') { | 
|  | mappingOption = zrUtil.clone(mappingOption); | 
|  | mappingOption.type = 'colorAlpha'; | 
|  | mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption); | 
|  | } | 
|  | }); | 
|  | }); | 
|  |  | 
|  | return visualMappings; | 
|  |  | 
|  | function createMappings() { | 
|  | const Creater = function () {}; | 
|  | // Make sure hidden fields will not be visited by | 
|  | // object iteration (with hasOwnProperty checking). | 
|  | Creater.prototype.__hidden = Creater.prototype; | 
|  | const obj = new (Creater as any)(); | 
|  | return obj; | 
|  | } | 
|  | } | 
|  |  | 
|  | export function replaceVisualOption<T extends string>( | 
|  | thisOption: Partial<Record<T, any>>, newOption: Partial<Record<T, any>>, keys: readonly T[] | 
|  | ) { | 
|  | // Visual attributes merge is not supported, otherwise it | 
|  | // brings overcomplicated merge logic. See #2853. So if | 
|  | // newOption has anyone of these keys, all of these keys | 
|  | // will be reset. Otherwise, all keys remain. | 
|  | let has; | 
|  | zrUtil.each(keys, function (key) { | 
|  | if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { | 
|  | has = true; | 
|  | } | 
|  | }); | 
|  | has && zrUtil.each(keys, function (key) { | 
|  | if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { | 
|  | thisOption[key] = zrUtil.clone(newOption[key]); | 
|  | } | 
|  | else { | 
|  | delete thisOption[key]; | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param stateList | 
|  | * @param visualMappings | 
|  | * @param list | 
|  | * @param getValueState param: valueOrIndex, return: state. | 
|  | * @param scope Scope for getValueState | 
|  | * @param dimension Concrete dimension, if used. | 
|  | */ | 
|  | // ???! handle brush? | 
|  | export function applyVisual<VisualState extends string, Scope>( | 
|  | stateList: readonly VisualState[], | 
|  | visualMappings: VisualMappingCollection<VisualState>, | 
|  | data: SeriesData, | 
|  | getValueState: (this: Scope, valueOrIndex: ParsedValue | number) => VisualState, | 
|  | scope?: Scope, | 
|  | dimension?: DimensionLoose | 
|  | ) { | 
|  | const visualTypesMap: Partial<Record<VisualState, BuiltinVisualProperty[]>> = {}; | 
|  | zrUtil.each(stateList, function (state) { | 
|  | const visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); | 
|  | visualTypesMap[state] = visualTypes; | 
|  | }); | 
|  |  | 
|  | let dataIndex: number; | 
|  |  | 
|  | function getVisual(key: string) { | 
|  | return getItemVisualFromData(data, dataIndex, key) as string | number; | 
|  | } | 
|  |  | 
|  | function setVisual(key: string, value: any) { | 
|  | setItemVisualFromData(data, dataIndex, key, value); | 
|  | } | 
|  |  | 
|  | if (dimension == null) { | 
|  | data.each(eachItem); | 
|  | } | 
|  | else { | 
|  | data.each([dimension], eachItem); | 
|  | } | 
|  |  | 
|  | function eachItem(valueOrIndex: ParsedValue | number, index?: number) { | 
|  | dataIndex = dimension == null | 
|  | ? valueOrIndex as number    // First argument is index | 
|  | : index; | 
|  |  | 
|  | const rawDataItem = data.getRawDataItem(dataIndex); | 
|  | // Consider performance | 
|  | // @ts-ignore | 
|  | if (rawDataItem && rawDataItem.visualMap === false) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const valueState = getValueState.call(scope, valueOrIndex); | 
|  | const mappings = visualMappings[valueState]; | 
|  | const visualTypes = visualTypesMap[valueState]; | 
|  |  | 
|  | for (let i = 0, len = visualTypes.length; i < len; i++) { | 
|  | const type = visualTypes[i]; | 
|  | mappings[type] && mappings[type].applyVisual( | 
|  | valueOrIndex, getVisual, setVisual | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param data | 
|  | * @param stateList | 
|  | * @param visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>> | 
|  | * @param getValueState param: valueOrIndex, return: state. | 
|  | * @param dim dimension or dimension index. | 
|  | */ | 
|  | export function incrementalApplyVisual<VisualState extends string>( | 
|  | stateList: readonly VisualState[], | 
|  | visualMappings: VisualMappingCollection<VisualState>, | 
|  | getValueState: (valueOrIndex: ParsedValue | number) => VisualState, | 
|  | dim?: DimensionLoose | 
|  | ): StageHandlerProgressExecutor { | 
|  | const visualTypesMap: Partial<Record<VisualState, BuiltinVisualProperty[]>> = {}; | 
|  | zrUtil.each(stateList, function (state) { | 
|  | const visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); | 
|  | visualTypesMap[state] = visualTypes; | 
|  | }); | 
|  |  | 
|  | return { | 
|  | progress: function progress(params, data) { | 
|  | let dimIndex: DimensionIndex; | 
|  | if (dim != null) { | 
|  | dimIndex = data.getDimensionIndex(dim); | 
|  | } | 
|  |  | 
|  | function getVisual(key: string) { | 
|  | return getItemVisualFromData(data, dataIndex, key) as string | number; | 
|  | } | 
|  |  | 
|  | function setVisual(key: string, value: any) { | 
|  | setItemVisualFromData(data, dataIndex, key, value); | 
|  | } | 
|  |  | 
|  | let dataIndex: number; | 
|  | const store = data.getStore(); | 
|  | while ((dataIndex = params.next()) != null) { | 
|  | const rawDataItem = data.getRawDataItem(dataIndex); | 
|  |  | 
|  | // Consider performance | 
|  | // @ts-ignore | 
|  | if (rawDataItem && rawDataItem.visualMap === false) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const value = dim != null | 
|  | ? store.get(dimIndex, dataIndex) | 
|  | : dataIndex; | 
|  |  | 
|  | const valueState = getValueState(value); | 
|  | const mappings = visualMappings[valueState]; | 
|  | const visualTypes = visualTypesMap[valueState]; | 
|  |  | 
|  | for (let i = 0, len = visualTypes.length; i < len; i++) { | 
|  | const type = visualTypes[i]; | 
|  | mappings[type] && mappings[type].applyVisual(value, getVisual, setVisual); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  | } |