| /* | 
 | * 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. | 
 | */ | 
 |  | 
 | import * as zrUtil from 'zrender/src/core/util'; | 
 | import { DisplayableState } from 'zrender/src/graphic/Displayable'; | 
 | import { PathStyleProps } from 'zrender/src/graphic/Path'; | 
 | import { parse, stringify } from 'zrender/src/tool/color'; | 
 | import * as graphic from '../../util/graphic'; | 
 | import { enableHoverEmphasis } from '../../util/states'; | 
 | import {setLabelStyle, createTextStyle} from '../../label/labelStyle'; | 
 | import {makeBackground} from '../helper/listComponent'; | 
 | import * as layoutUtil from '../../util/layout'; | 
 | import ComponentView from '../../view/Component'; | 
 | import LegendModel, { | 
 |     LegendItemStyleOption, | 
 |     LegendLineStyleOption, | 
 |     LegendOption, | 
 |     LegendSelectorButtonOption, | 
 |     LegendIconParams, | 
 |     LegendTooltipFormatterParams | 
 | } from './LegendModel'; | 
 | import GlobalModel from '../../model/Global'; | 
 | import ExtensionAPI from '../../core/ExtensionAPI'; | 
 | import { | 
 |     ZRTextAlign, | 
 |     ZRRectLike, | 
 |     CommonTooltipOption, | 
 |     ColorString, | 
 |     SeriesOption, | 
 |     SymbolOptionMixin | 
 | } from '../../util/types'; | 
 | import Model from '../../model/Model'; | 
 | import {LineStyleProps} from '../../model/mixin/lineStyle'; | 
 | import {createSymbol, ECSymbol} from '../../util/symbol'; | 
 | import SeriesModel from '../../model/Series'; | 
 | import { createOrUpdatePatternFromDecal } from '../../util/decal'; | 
 |  | 
 | const curry = zrUtil.curry; | 
 | const each = zrUtil.each; | 
 | const Group = graphic.Group; | 
 |  | 
 | class LegendView extends ComponentView { | 
 |     static type = 'legend.plain'; | 
 |     type = LegendView.type; | 
 |  | 
 |     newlineDisabled = false; | 
 |  | 
 |     private _contentGroup: graphic.Group; | 
 |  | 
 |     private _backgroundEl: graphic.Rect; | 
 |  | 
 |     private _selectorGroup: graphic.Group; | 
 |  | 
 |     /** | 
 |      * If first rendering, `contentGroup.position` is [0, 0], which | 
 |      * does not make sense and may cause unexpected animation if adopted. | 
 |      */ | 
 |     private _isFirstRender: boolean; | 
 |  | 
 |     init() { | 
 |  | 
 |         this.group.add(this._contentGroup = new Group()); | 
 |         this.group.add(this._selectorGroup = new Group()); | 
 |  | 
 |         this._isFirstRender = true; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @protected | 
 |      */ | 
 |     getContentGroup() { | 
 |         return this._contentGroup; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @protected | 
 |      */ | 
 |     getSelectorGroup() { | 
 |         return this._selectorGroup; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @override | 
 |      */ | 
 |     render( | 
 |         legendModel: LegendModel, | 
 |         ecModel: GlobalModel, | 
 |         api: ExtensionAPI | 
 |     ) { | 
 |         const isFirstRender = this._isFirstRender; | 
 |         this._isFirstRender = false; | 
 |  | 
 |         this.resetInner(); | 
 |  | 
 |         if (!legendModel.get('show', true)) { | 
 |             return; | 
 |         } | 
 |  | 
 |         let itemAlign = legendModel.get('align'); | 
 |         const orient = legendModel.get('orient'); | 
 |         if (!itemAlign || itemAlign === 'auto') { | 
 |             itemAlign = ( | 
 |                 legendModel.get('left') === 'right' | 
 |                 && orient === 'vertical' | 
 |             ) ? 'right' : 'left'; | 
 |         } | 
 |  | 
 |         // selector has been normalized to an array in model | 
 |         const selector = legendModel.get('selector', true) as LegendSelectorButtonOption[]; | 
 |         let selectorPosition = legendModel.get('selectorPosition', true); | 
 |         if (selector && (!selectorPosition || selectorPosition === 'auto')) { | 
 |             selectorPosition = orient === 'horizontal' ? 'end' : 'start'; | 
 |         } | 
 |  | 
 |         this.renderInner(itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); | 
 |  | 
 |         // Perform layout. | 
 |         const positionInfo = legendModel.getBoxLayoutParams(); | 
 |         const viewportSize = {width: api.getWidth(), height: api.getHeight()}; | 
 |         const padding = legendModel.get('padding'); | 
 |  | 
 |         const maxSize = layoutUtil.getLayoutRect(positionInfo, viewportSize, padding); | 
 |  | 
 |         const mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition); | 
 |  | 
 |         // Place mainGroup, based on the calculated `mainRect`. | 
 |         const layoutRect = layoutUtil.getLayoutRect( | 
 |             zrUtil.defaults({ | 
 |                 width: mainRect.width, | 
 |                 height: mainRect.height | 
 |             }, positionInfo), | 
 |             viewportSize, | 
 |             padding | 
 |         ); | 
 |         this.group.x = layoutRect.x - mainRect.x; | 
 |         this.group.y = layoutRect.y - mainRect.y; | 
 |         this.group.markRedraw(); | 
 |  | 
 |         // Render background after group is layout. | 
 |         this.group.add( | 
 |             this._backgroundEl = makeBackground(mainRect, legendModel) | 
 |         ); | 
 |     } | 
 |  | 
 |     protected resetInner() { | 
 |         this.getContentGroup().removeAll(); | 
 |         this._backgroundEl && this.group.remove(this._backgroundEl); | 
 |         this.getSelectorGroup().removeAll(); | 
 |     } | 
 |  | 
 |     protected renderInner( | 
 |         itemAlign: LegendOption['align'], | 
 |         legendModel: LegendModel, | 
 |         ecModel: GlobalModel, | 
 |         api: ExtensionAPI, | 
 |         selector: LegendSelectorButtonOption[], | 
 |         orient: LegendOption['orient'], | 
 |         selectorPosition: LegendOption['selectorPosition'] | 
 |     ) { | 
 |         const contentGroup = this.getContentGroup(); | 
 |         const legendDrawnMap = zrUtil.createHashMap(); | 
 |         const selectMode = legendModel.get('selectedMode'); | 
 |  | 
 |         const excludeSeriesId: string[] = []; | 
 |         ecModel.eachRawSeries(function (seriesModel) { | 
 |             !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id); | 
 |         }); | 
 |  | 
 |         each(legendModel.getData(), function (legendItemModel, dataIndex) { | 
 |             const name = legendItemModel.get('name'); | 
 |  | 
 |             // Use empty string or \n as a newline string | 
 |             if (!this.newlineDisabled && (name === '' || name === '\n')) { | 
 |                 const g = new Group(); | 
 |                 // @ts-ignore | 
 |                 g.newline = true; | 
 |                 contentGroup.add(g); | 
 |                 return; | 
 |             } | 
 |  | 
 |             // Representitive series. | 
 |             const seriesModel = ecModel.getSeriesByName(name)[0] as | 
 |                 SeriesModel<SeriesOption & SymbolOptionMixin>; | 
 |  | 
 |             if (legendDrawnMap.get(name)) { | 
 |                 // Have been drawn | 
 |                 return; | 
 |             } | 
 |  | 
 |             // Legend to control series. | 
 |             if (seriesModel) { | 
 |                 const data = seriesModel.getData(); | 
 |                 const lineVisualStyle = data.getVisual('legendLineStyle') || {}; | 
 |                 const legendIcon = data.getVisual('legendIcon'); | 
 |  | 
 |                 /** | 
 |                  * `data.getVisual('style')` may be the color from the register | 
 |                  * in series. For example, for line series, | 
 |                  */ | 
 |                 const style = data.getVisual('style'); | 
 |  | 
 |                 const itemGroup = this._createItem( | 
 |                     seriesModel, name, dataIndex, | 
 |                     legendItemModel, legendModel, itemAlign, | 
 |                     lineVisualStyle, style, legendIcon, selectMode, api | 
 |                 ); | 
 |  | 
 |                 itemGroup.on('click', curry(dispatchSelectAction, name, null, api, excludeSeriesId)) | 
 |                     .on('mouseover', curry(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId)) | 
 |                     .on('mouseout', curry(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId)); | 
 |  | 
 |                 legendDrawnMap.set(name, true); | 
 |             } | 
 |             else { | 
 |                 // Legend to control data. In pie and funnel. | 
 |                 ecModel.eachRawSeries(function (seriesModel) { | 
 |  | 
 |                     // In case multiple series has same data name | 
 |                     if (legendDrawnMap.get(name)) { | 
 |                         return; | 
 |                     } | 
 |  | 
 |                     if (seriesModel.legendVisualProvider) { | 
 |                         const provider = seriesModel.legendVisualProvider; | 
 |                         if (!provider.containName(name)) { | 
 |                             return; | 
 |                         } | 
 |  | 
 |                         const idx = provider.indexOfName(name); | 
 |  | 
 |                         let style = provider.getItemVisual(idx, 'style') as PathStyleProps; | 
 |                         const legendIcon = provider.getItemVisual(idx, 'legendIcon'); | 
 |  | 
 |                         const colorArr = parse(style.fill as ColorString); | 
 |                         // Color may be set to transparent in visualMap when data is out of range. | 
 |                         // Do not show nothing. | 
 |                         if (colorArr && colorArr[3] === 0) { | 
 |                             colorArr[3] = 0.2; | 
 |                             // TODO color is set to 0, 0, 0, 0. Should show correct RGBA | 
 |                             style = zrUtil.extend(zrUtil.extend({}, style), { fill: stringify(colorArr, 'rgba') }); | 
 |                         } | 
 |  | 
 |                         const itemGroup = this._createItem( | 
 |                             seriesModel, name, dataIndex, | 
 |                             legendItemModel, legendModel, itemAlign, | 
 |                             {}, style, legendIcon, selectMode, api | 
 |                         ); | 
 |  | 
 |                         // FIXME: consider different series has items with the same name. | 
 |                         itemGroup.on('click', curry(dispatchSelectAction, null, name, api, excludeSeriesId)) | 
 |                             // Should not specify the series name, consider legend controls | 
 |                             // more than one pie series. | 
 |                             .on('mouseover', curry(dispatchHighlightAction, null, name, api, excludeSeriesId)) | 
 |                             .on('mouseout', curry(dispatchDownplayAction, null, name, api, excludeSeriesId)); | 
 |  | 
 |                         legendDrawnMap.set(name, true); | 
 |                     } | 
 |  | 
 |                 }, this); | 
 |             } | 
 |  | 
 |             if (__DEV__) { | 
 |                 if (!legendDrawnMap.get(name)) { | 
 |                     console.warn( | 
 |                         name + ' series not exists. Legend data should be same with series name or data name.' | 
 |                     ); | 
 |                 } | 
 |             } | 
 |         }, this); | 
 |  | 
 |         if (selector) { | 
 |             this._createSelector(selector, legendModel, api, orient, selectorPosition); | 
 |         } | 
 |     } | 
 |  | 
 |     private _createSelector( | 
 |         selector: LegendSelectorButtonOption[], | 
 |         legendModel: LegendModel, | 
 |         api: ExtensionAPI, | 
 |         orient: LegendOption['orient'], | 
 |         selectorPosition: LegendOption['selectorPosition'] | 
 |     ) { | 
 |         const selectorGroup = this.getSelectorGroup(); | 
 |  | 
 |         each(selector, function createSelectorButton(selectorItem) { | 
 |             const type = selectorItem.type; | 
 |  | 
 |             const labelText = new graphic.Text({ | 
 |                 style: { | 
 |                     x: 0, | 
 |                     y: 0, | 
 |                     align: 'center', | 
 |                     verticalAlign: 'middle' | 
 |                 }, | 
 |                 onclick() { | 
 |                     api.dispatchAction({ | 
 |                         type: type === 'all' ? 'legendAllSelect' : 'legendInverseSelect' | 
 |                     }); | 
 |                 } | 
 |             }); | 
 |  | 
 |             selectorGroup.add(labelText); | 
 |  | 
 |             const labelModel = legendModel.getModel('selectorLabel'); | 
 |             const emphasisLabelModel = legendModel.getModel(['emphasis', 'selectorLabel']); | 
 |  | 
 |             setLabelStyle( | 
 |                 labelText, { normal: labelModel, emphasis: emphasisLabelModel }, | 
 |                 { | 
 |                     defaultText: selectorItem.title | 
 |                 } | 
 |             ); | 
 |             enableHoverEmphasis(labelText); | 
 |         }); | 
 |     } | 
 |  | 
 |     private _createItem( | 
 |         seriesModel: SeriesModel<SeriesOption & SymbolOptionMixin>, | 
 |         name: string, | 
 |         dataIndex: number, | 
 |         legendItemModel: LegendModel['_data'][number], | 
 |         legendModel: LegendModel, | 
 |         itemAlign: LegendOption['align'], | 
 |         lineVisualStyle: LineStyleProps, | 
 |         itemVisualStyle: PathStyleProps, | 
 |         legendIcon: string, | 
 |         selectMode: LegendOption['selectedMode'], | 
 |         api: ExtensionAPI | 
 |     ) { | 
 |         const drawType = seriesModel.visualDrawType; | 
 |         const itemWidth = legendModel.get('itemWidth'); | 
 |         const itemHeight = legendModel.get('itemHeight'); | 
 |         const isSelected = legendModel.isSelected(name); | 
 |  | 
 |         const iconRotate = legendItemModel.get('symbolRotate'); | 
 |         const symbolKeepAspect = legendItemModel.get('symbolKeepAspect'); | 
 |  | 
 |         const legendIconType = legendItemModel.get('icon'); | 
 |         legendIcon = legendIconType || legendIcon || 'roundRect'; | 
 |  | 
 |         const style = getLegendStyle( | 
 |             legendIcon, | 
 |             legendItemModel, | 
 |             lineVisualStyle, | 
 |             itemVisualStyle, | 
 |             drawType, | 
 |             isSelected, | 
 |             api | 
 |         ); | 
 |  | 
 |         const itemGroup = new Group(); | 
 |  | 
 |         const textStyleModel = legendItemModel.getModel('textStyle'); | 
 |  | 
 |         if (zrUtil.isFunction(seriesModel.getLegendIcon) | 
 |             && (!legendIconType || legendIconType === 'inherit') | 
 |         ) { | 
 |             // Series has specific way to define legend icon | 
 |             itemGroup.add(seriesModel.getLegendIcon({ | 
 |                 itemWidth, | 
 |                 itemHeight, | 
 |                 icon: legendIcon, | 
 |                 iconRotate: iconRotate, | 
 |                 itemStyle: style.itemStyle, | 
 |                 lineStyle: style.lineStyle, | 
 |                 symbolKeepAspect | 
 |             })); | 
 |         } | 
 |         else { | 
 |             // Use default legend icon policy for most series | 
 |             const rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol') | 
 |                 ? (iconRotate === 'inherit' | 
 |                     ? seriesModel.getData().getVisual('symbolRotate') | 
 |                     : iconRotate | 
 |                 ) | 
 |                 : 0; // No rotation for no icon | 
 |             itemGroup.add(getDefaultLegendIcon({ | 
 |                 itemWidth, | 
 |                 itemHeight, | 
 |                 icon: legendIcon, | 
 |                 iconRotate: rotate, | 
 |                 itemStyle: style.itemStyle, | 
 |                 lineStyle: style.lineStyle, | 
 |                 symbolKeepAspect | 
 |             })); | 
 |         } | 
 |  | 
 |         const textX = itemAlign === 'left' ? itemWidth + 5 : -5; | 
 |         const textAlign = itemAlign as ZRTextAlign; | 
 |  | 
 |         const formatter = legendModel.get('formatter'); | 
 |         let content = name; | 
 |         if (zrUtil.isString(formatter) && formatter) { | 
 |             content = formatter.replace('{name}', name != null ? name : ''); | 
 |         } | 
 |         else if (zrUtil.isFunction(formatter)) { | 
 |             content = formatter(name); | 
 |         } | 
 |  | 
 |         const inactiveColor = legendItemModel.get('inactiveColor'); | 
 |         itemGroup.add(new graphic.Text({ | 
 |             style: createTextStyle(textStyleModel, { | 
 |                 text: content, | 
 |                 x: textX, | 
 |                 y: itemHeight / 2, | 
 |                 fill: isSelected ? textStyleModel.getTextColor() : inactiveColor, | 
 |                 align: textAlign, | 
 |                 verticalAlign: 'middle' | 
 |             }) | 
 |         })); | 
 |  | 
 |         // Add a invisible rect to increase the area of mouse hover | 
 |         const hitRect = new graphic.Rect({ | 
 |             shape: itemGroup.getBoundingRect(), | 
 |             invisible: true | 
 |         }); | 
 |  | 
 |         const tooltipModel = | 
 |             legendItemModel.getModel('tooltip') as Model<CommonTooltipOption<LegendTooltipFormatterParams>>; | 
 |         if (tooltipModel.get('show')) { | 
 |             graphic.setTooltipConfig({ | 
 |                 el: hitRect, | 
 |                 componentModel: legendModel, | 
 |                 itemName: name, | 
 |                 itemTooltipOption: tooltipModel.option | 
 |             }); | 
 |         } | 
 |         itemGroup.add(hitRect); | 
 |  | 
 |         itemGroup.eachChild(function (child) { | 
 |             child.silent = true; | 
 |         }); | 
 |  | 
 |         hitRect.silent = !selectMode; | 
 |  | 
 |         this.getContentGroup().add(itemGroup); | 
 |  | 
 |         enableHoverEmphasis(itemGroup); | 
 |  | 
 |         // @ts-ignore | 
 |         itemGroup.__legendDataIndex = dataIndex; | 
 |  | 
 |         return itemGroup; | 
 |     } | 
 |  | 
 |     protected layoutInner( | 
 |         legendModel: LegendModel, | 
 |         itemAlign: LegendOption['align'], | 
 |         maxSize: { width: number, height: number }, | 
 |         isFirstRender: boolean, | 
 |         selector: LegendOption['selector'], | 
 |         selectorPosition: LegendOption['selectorPosition'] | 
 |     ): ZRRectLike { | 
 |         const contentGroup = this.getContentGroup(); | 
 |         const selectorGroup = this.getSelectorGroup(); | 
 |  | 
 |         // Place items in contentGroup. | 
 |         layoutUtil.box( | 
 |             legendModel.get('orient'), | 
 |             contentGroup, | 
 |             legendModel.get('itemGap'), | 
 |             maxSize.width, | 
 |             maxSize.height | 
 |         ); | 
 |  | 
 |         const contentRect = contentGroup.getBoundingRect(); | 
 |         const contentPos = [-contentRect.x, -contentRect.y]; | 
 |  | 
 |         selectorGroup.markRedraw(); | 
 |         contentGroup.markRedraw(); | 
 |  | 
 |         if (selector) { | 
 |             // Place buttons in selectorGroup | 
 |             layoutUtil.box( | 
 |                 // Buttons in selectorGroup always layout horizontally | 
 |                 'horizontal', | 
 |                 selectorGroup, | 
 |                 legendModel.get('selectorItemGap', true) | 
 |             ); | 
 |  | 
 |             const selectorRect = selectorGroup.getBoundingRect(); | 
 |             const selectorPos = [-selectorRect.x, -selectorRect.y]; | 
 |             const selectorButtonGap = legendModel.get('selectorButtonGap', true); | 
 |  | 
 |             const orientIdx = legendModel.getOrient().index; | 
 |             const wh: 'width' | 'height' = orientIdx === 0 ? 'width' : 'height'; | 
 |             const hw: 'width' | 'height' = orientIdx === 0 ? 'height' : 'width'; | 
 |             const yx: 'x' | 'y' = orientIdx === 0 ? 'y' : 'x'; | 
 |  | 
 |             if (selectorPosition === 'end') { | 
 |                 selectorPos[orientIdx] += contentRect[wh] + selectorButtonGap; | 
 |             } | 
 |             else { | 
 |                 contentPos[orientIdx] += selectorRect[wh] + selectorButtonGap; | 
 |             } | 
 |  | 
 |             // Always align selector to content as 'middle' | 
 |             selectorPos[1 - orientIdx] += contentRect[hw] / 2 - selectorRect[hw] / 2; | 
 |             selectorGroup.x = selectorPos[0]; | 
 |             selectorGroup.y = selectorPos[1]; | 
 |             contentGroup.x = contentPos[0]; | 
 |             contentGroup.y = contentPos[1]; | 
 |  | 
 |             const mainRect = {x: 0, y: 0} as ZRRectLike; | 
 |             mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh]; | 
 |             mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw]); | 
 |             mainRect[yx] = Math.min(0, selectorRect[yx] + selectorPos[1 - orientIdx]); | 
 |             return mainRect; | 
 |         } | 
 |         else { | 
 |             contentGroup.x = contentPos[0]; | 
 |             contentGroup.y = contentPos[1]; | 
 |             return this.group.getBoundingRect(); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * @protected | 
 |      */ | 
 |     remove() { | 
 |         this.getContentGroup().removeAll(); | 
 |         this._isFirstRender = true; | 
 |     } | 
 |  | 
 | } | 
 |  | 
 | function getLegendStyle( | 
 |     iconType: string, | 
 |     legendItemModel: LegendModel['_data'][number], | 
 |     lineVisualStyle: PathStyleProps, | 
 |     itemVisualStyle: PathStyleProps, | 
 |     drawType: 'fill' | 'stroke', | 
 |     isSelected: boolean, | 
 |     api: ExtensionAPI | 
 | ) { | 
 |     /** | 
 |      * Use series style if is inherit; | 
 |      * elsewise, use legend style | 
 |      */ | 
 |     function handleCommonProps(style: PathStyleProps, visualStyle: PathStyleProps) { | 
 |         // If lineStyle.width is 'auto', it is set to be 2 if series has border | 
 |         if ((style.lineWidth as any) === 'auto') { | 
 |             style.lineWidth = (visualStyle.lineWidth > 0) ? 2 : 0; | 
 |         } | 
 |  | 
 |         each(style, (propVal, propName) => { | 
 |             style[propName] === 'inherit' && ((style as any)[propName] = visualStyle[propName]); | 
 |         }); | 
 |     } | 
 |  | 
 |     // itemStyle | 
 |     const itemStyleModel = legendItemModel.getModel('itemStyle') as Model<LegendItemStyleOption>; | 
 |     const itemStyle = itemStyleModel.getItemStyle(); | 
 |     const iconBrushType = iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke'; | 
 |     const decalStyle = itemStyleModel.getShallow('decal'); | 
 |     itemStyle.decal = (!decalStyle || decalStyle === 'inherit') | 
 |                     ? itemVisualStyle.decal | 
 |                     : createOrUpdatePatternFromDecal(decalStyle, api); | 
 |  | 
 |     if (itemStyle.fill === 'inherit') { | 
 |         /** | 
 |          * Series with visualDrawType as 'stroke' should have | 
 |          * series stroke as legend fill | 
 |          */ | 
 |         itemStyle.fill = itemVisualStyle[drawType]; | 
 |     } | 
 |     if (itemStyle.stroke === 'inherit') { | 
 |         /** | 
 |          * icon type with "emptyXXX" should use fill color | 
 |          * in visual style | 
 |          */ | 
 |         itemStyle.stroke = itemVisualStyle[iconBrushType]; | 
 |     } | 
 |     if ((itemStyle.opacity as any) === 'inherit') { | 
 |         /** | 
 |          * Use lineStyle.opacity if drawType is stroke | 
 |          */ | 
 |         itemStyle.opacity = (drawType === 'fill' ? itemVisualStyle : lineVisualStyle).opacity; | 
 |     } | 
 |     handleCommonProps(itemStyle, itemVisualStyle); | 
 |  | 
 |     // lineStyle | 
 |     const legendLineModel = legendItemModel.getModel('lineStyle') as Model<LegendLineStyleOption>; | 
 |     const lineStyle: LineStyleProps = legendLineModel.getLineStyle(); | 
 |     handleCommonProps(lineStyle, lineVisualStyle); | 
 |  | 
 |     // Fix auto color to real color | 
 |     (itemStyle.fill === 'auto') && (itemStyle.fill = itemVisualStyle.fill); | 
 |     (itemStyle.stroke === 'auto') && (itemStyle.stroke = itemVisualStyle.fill); | 
 |     (lineStyle.stroke === 'auto') && (lineStyle.stroke = itemVisualStyle.fill); | 
 |  | 
 |     if (!isSelected) { | 
 |         const borderWidth = legendItemModel.get('inactiveBorderWidth'); | 
 |         /** | 
 |          * Since stroke is set to be inactiveBorderColor, it may occur that | 
 |          * there is no border in series but border in legend, so we need to | 
 |          * use border only when series has border if is set to be auto | 
 |          */ | 
 |         const visualHasBorder = itemStyle[iconBrushType]; | 
 |         itemStyle.lineWidth = borderWidth === 'auto' | 
 |             ? (itemVisualStyle.lineWidth > 0 && visualHasBorder ? 2 : 0) | 
 |             : itemStyle.lineWidth; | 
 |         itemStyle.fill = legendItemModel.get('inactiveColor'); | 
 |         itemStyle.stroke = legendItemModel.get('inactiveBorderColor'); | 
 |         lineStyle.stroke = legendLineModel.get('inactiveColor'); | 
 |         lineStyle.lineWidth = legendLineModel.get('inactiveWidth'); | 
 |     } | 
 |     return { itemStyle, lineStyle }; | 
 | } | 
 |  | 
 | function getDefaultLegendIcon(opt: LegendIconParams): ECSymbol { | 
 |     const symboType = opt.icon || 'roundRect'; | 
 |     const icon = createSymbol( | 
 |         symboType, | 
 |         0, | 
 |         0, | 
 |         opt.itemWidth, | 
 |         opt.itemHeight, | 
 |         opt.itemStyle.fill, | 
 |         opt.symbolKeepAspect | 
 |     ); | 
 |  | 
 |     icon.setStyle(opt.itemStyle); | 
 |  | 
 |     icon.rotation = (opt.iconRotate as number || 0) * Math.PI / 180; | 
 |     icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]); | 
 |  | 
 |     if (symboType.indexOf('empty') > -1) { | 
 |         icon.style.stroke = icon.style.fill; | 
 |         icon.style.fill = '#fff'; | 
 |         icon.style.lineWidth = 2; | 
 |     } | 
 |  | 
 |     return icon; | 
 | } | 
 |  | 
 | function dispatchSelectAction( | 
 |     seriesName: string, | 
 |     dataName: string, | 
 |     api: ExtensionAPI, | 
 |     excludeSeriesId: string[] | 
 | ) { | 
 |     // downplay before unselect | 
 |     dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId); | 
 |     api.dispatchAction({ | 
 |         type: 'legendToggleSelect', | 
 |         name: seriesName != null ? seriesName : dataName | 
 |     }); | 
 |     // highlight after select | 
 |     // TODO highlight immediately may cause animation loss. | 
 |     dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId); | 
 | } | 
 |  | 
 | function isUseHoverLayer(api: ExtensionAPI) { | 
 |     const list = api.getZr().storage.getDisplayList(); | 
 |     let emphasisState: DisplayableState; | 
 |     let i = 0; | 
 |     const len = list.length; | 
 |     while (i < len && !(emphasisState = list[i].states.emphasis)) { | 
 |         i++; | 
 |     } | 
 |     return emphasisState && emphasisState.hoverLayer; | 
 | } | 
 |  | 
 | function dispatchHighlightAction( | 
 |     seriesName: string, | 
 |     dataName: string, | 
 |     api: ExtensionAPI, | 
 |     excludeSeriesId: string[] | 
 | ) { | 
 |     // If element hover will move to a hoverLayer. | 
 |     if (!isUseHoverLayer(api)) { | 
 |         api.dispatchAction({ | 
 |             type: 'highlight', | 
 |             seriesName: seriesName, | 
 |             name: dataName, | 
 |             excludeSeriesId: excludeSeriesId | 
 |         }); | 
 |     } | 
 | } | 
 |  | 
 | function dispatchDownplayAction( | 
 |     seriesName: string, | 
 |     dataName: string, | 
 |     api: ExtensionAPI, | 
 |     excludeSeriesId: string[] | 
 | ) { | 
 |     // If element hover will move to a hoverLayer. | 
 |     if (!isUseHoverLayer(api)) { | 
 |         api.dispatchAction({ | 
 |             type: 'downplay', | 
 |             seriesName: seriesName, | 
 |             name: dataName, | 
 |             excludeSeriesId: excludeSeriesId | 
 |         }); | 
 |     } | 
 | } | 
 |  | 
 | export default LegendView; |