|  | /* | 
|  | * 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 {Dictionary} from 'zrender/src/core/types'; | 
|  | import {makeInner, normalizeToArray} from '../../util/model'; | 
|  | import Model from '../Model'; | 
|  | import {ZRColor, PaletteOptionMixin, DecalObject, AriaOptionMixin} from '../../util/types'; | 
|  | import GlobalModel from '../Global'; | 
|  |  | 
|  | type Inner<T> = (hostObj: PaletteMixin<PaletteOptionMixin>) => { | 
|  | paletteIdx: number; | 
|  | paletteNameMap: Dictionary<T>; | 
|  | }; | 
|  |  | 
|  | const innerColor: Inner<ZRColor> = makeInner<{ | 
|  | paletteIdx: number | 
|  | paletteNameMap: Dictionary<ZRColor> | 
|  | }, PaletteMixin>(); | 
|  |  | 
|  | const innerDecal: Inner<DecalObject> = makeInner<{ | 
|  | paletteIdx: number | 
|  | paletteNameMap: Dictionary<DecalObject> | 
|  | }, PaletteMixin>(); | 
|  |  | 
|  |  | 
|  |  | 
|  | interface PaletteMixin<T extends PaletteOptionMixin = PaletteOptionMixin> | 
|  | extends Pick<Model<T>, 'get'> {} | 
|  |  | 
|  | class PaletteMixin<T extends PaletteOptionMixin = PaletteOptionMixin> { | 
|  | getColorFromPalette( | 
|  | this: PaletteMixin<T>, | 
|  | name: string, | 
|  | scope?: any, | 
|  | requestNum?: number | 
|  | ): ZRColor { | 
|  | const defaultPalette = normalizeToArray(this.get('color', true)); | 
|  | const layeredPalette = this.get('colorLayer', true); | 
|  | return getFromPalette<ZRColor>(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum); | 
|  | } | 
|  |  | 
|  | clearColorPalette(this: PaletteMixin<T>) { | 
|  | clearPalette<ZRColor>(this, innerColor); | 
|  | } | 
|  | } | 
|  |  | 
|  | export function getDecalFromPalette( | 
|  | ecModel: GlobalModel, | 
|  | name: string, | 
|  | scope?: any, | 
|  | requestNum?: number | 
|  | ): DecalObject { | 
|  | const defaultDecals = normalizeToArray((ecModel as Model<AriaOptionMixin>).get(['aria', 'decal', 'decals'])); | 
|  | return getFromPalette<DecalObject>(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum); | 
|  | } | 
|  |  | 
|  |  | 
|  | function getNearestPalette<T>( | 
|  | palettes: T[][], requestColorNum: number | 
|  | ): T[] { | 
|  | const paletteNum = palettes.length; | 
|  | // TODO palettes must be in order | 
|  | for (let i = 0; i < paletteNum; i++) { | 
|  | if (palettes[i].length > requestColorNum) { | 
|  | return palettes[i]; | 
|  | } | 
|  | } | 
|  | return palettes[paletteNum - 1]; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param name MUST NOT be null/undefined. Otherwise call this function | 
|  | *             twise with the same parameters will get different result. | 
|  | * @param scope default this. | 
|  | * @return Can be null/undefined | 
|  | */ | 
|  | function getFromPalette<T>( | 
|  | that: PaletteMixin, | 
|  | inner: Inner<T>, | 
|  | defaultPalette: T[], | 
|  | layeredPalette: T[][], | 
|  | name: string, | 
|  | scope?: any, | 
|  | requestNum?: number | 
|  | ): T { | 
|  | scope = scope || that; | 
|  | const scopeFields = inner(scope); | 
|  | const paletteIdx = scopeFields.paletteIdx || 0; | 
|  | const paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; | 
|  | // Use `hasOwnProperty` to avoid conflict with Object.prototype. | 
|  | if (paletteNameMap.hasOwnProperty(name)) { | 
|  | return paletteNameMap[name]; | 
|  | } | 
|  | let palette = ((requestNum == null || !layeredPalette) | 
|  | ? defaultPalette : getNearestPalette(layeredPalette, requestNum)); | 
|  |  | 
|  | // In case can't find in layered color palette. | 
|  | palette = palette || defaultPalette; | 
|  |  | 
|  | if (!palette || !palette.length) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const pickedPaletteItem = palette[paletteIdx]; | 
|  | if (name) { | 
|  | paletteNameMap[name] = pickedPaletteItem; | 
|  | } | 
|  | scopeFields.paletteIdx = (paletteIdx + 1) % palette.length; | 
|  |  | 
|  | return pickedPaletteItem; | 
|  | } | 
|  |  | 
|  | function clearPalette<T>(that: PaletteMixin, inner: Inner<T>) { | 
|  | inner(that).paletteIdx = 0; | 
|  | inner(that).paletteNameMap = {}; | 
|  | } | 
|  |  | 
|  | export {PaletteMixin}; |