/* * 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 { encodeHTML } from 'zrender/src/core/dom'; import { parseDate, isNumeric, numericToNumber } from './number'; import { TooltipRenderMode, ColorString, ZRColor, DimensionType } from './types'; import { Dictionary } from 'zrender/src/core/types'; import { GradientObject } from 'zrender/src/graphic/Gradient'; import { format as timeFormat, pad } from './time'; import { deprecateReplaceLog } from './log'; /** * Add a comma each three digit. */ export function addCommas(x: string | number): string { if (!isNumeric(x)) { return zrUtil.isString(x) ? x : '-'; } const parts = (x + '').split('.'); return parts[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,') + (parts.length > 1 ? ('.' + parts[1]) : ''); } export function toCamelCase(str: string, upperCaseFirst?: boolean): string { str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) { return group1.toUpperCase(); }); if (upperCaseFirst && str) { str = str.charAt(0).toUpperCase() + str.slice(1); } return str; } export const normalizeCssArray = zrUtil.normalizeCssArray; export { encodeHTML }; /** * Make value user readable for tooltip and label. * "User readable": * Try to not print programmer-specific text like NaN, Infinity, null, undefined. * Avoid to display an empty string, which users can not recognize there is * a value and it might look like a bug. */ export function makeValueReadable( value: unknown, valueType: DimensionType, useUTC: boolean ): string { const USER_READABLE_DEFUALT_TIME_PATTERN = '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}'; function stringToUserReadable(str: string): string { return (str && zrUtil.trim(str)) ? str : '-'; } function isNumberUserReadable(num: number): boolean { return !!(num != null && !isNaN(num) && isFinite(num)); } const isTypeTime = valueType === 'time'; const isValueDate = value instanceof Date; if (isTypeTime || isValueDate) { const date = isTypeTime ? parseDate(value) : value; if (!isNaN(+date)) { return timeFormat(date, USER_READABLE_DEFUALT_TIME_PATTERN, useUTC); } else if (isValueDate) { return '-'; } // In other cases, continue to try to display the value in the following code. } if (valueType === 'ordinal') { return zrUtil.isStringSafe(value) ? stringToUserReadable(value) : zrUtil.isNumber(value) ? (isNumberUserReadable(value) ? value + '' : '-') : '-'; } // By default. const numericResult = numericToNumber(value); return isNumberUserReadable(numericResult) ? addCommas(numericResult) : zrUtil.isStringSafe(value) ? stringToUserReadable(value) : typeof value === 'boolean' ? value + '' : '-'; } const TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; const wrapVar = function (varName: string, seriesIdx?: number): string { return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}'; }; export interface TplFormatterParam extends Dictionary { // Param name list for mapping `a`, `b`, `c`, `d`, `e` $vars: string[]; } /** * Template formatter * @param {Array.|Object} paramsList */ export function formatTpl( tpl: string, paramsList: TplFormatterParam | TplFormatterParam[], encode?: boolean ): string { if (!zrUtil.isArray(paramsList)) { paramsList = [paramsList]; } const seriesLen = paramsList.length; if (!seriesLen) { return ''; } const $vars = paramsList[0].$vars || []; for (let i = 0; i < $vars.length; i++) { const alias = TPL_VAR_ALIAS[i]; tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0)); } for (let seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) { for (let k = 0; k < $vars.length; k++) { const val = paramsList[seriesIdx][$vars[k]]; tpl = tpl.replace( wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val ); } } return tpl; } /** * simple Template formatter */ export function formatTplSimple(tpl: string, param: Dictionary, encode?: boolean) { zrUtil.each(param, function (value, key) { tpl = tpl.replace( '{' + key + '}', encode ? encodeHTML(value) : value ); }); return tpl; } interface RichTextTooltipMarker { renderMode: TooltipRenderMode; content: string; style: Dictionary; } export type TooltipMarker = string | RichTextTooltipMarker; export type TooltipMarkerType = 'item' | 'subItem'; interface GetTooltipMarkerOpt { color?: ColorString; extraCssText?: string; // By default: 'item' type?: TooltipMarkerType; renderMode?: TooltipRenderMode; // id name for marker. If only one marker is in a rich text, this can be omitted. // By default: 'markerX' markerId?: string; } // Only support color string export function getTooltipMarker(color: ColorString, extraCssText?: string): TooltipMarker; export function getTooltipMarker(opt: GetTooltipMarkerOpt): TooltipMarker; export function getTooltipMarker(inOpt: ColorString | GetTooltipMarkerOpt, extraCssText?: string): TooltipMarker { const opt = zrUtil.isString(inOpt) ? { color: inOpt, extraCssText: extraCssText } : (inOpt || {}) as GetTooltipMarkerOpt; const color = opt.color; const type = opt.type; extraCssText = opt.extraCssText; const renderMode = opt.renderMode || 'html'; if (!color) { return ''; } if (renderMode === 'html') { return type === 'subItem' ? '' : ''; } else { // Should better not to auto generate style name by auto-increment number here. // Because this util is usually called in tooltip formatter, which is probably // called repeatedly when mouse move and the auto-increment number increases fast. // Users can make their own style name by theirselves, make it unique and readable. const markerId = opt.markerId || 'markerX'; return { renderMode: renderMode, content: '{' + markerId + '|} ', style: type === 'subItem' ? { width: 4, height: 4, borderRadius: 2, backgroundColor: color } : { width: 10, height: 10, borderRadius: 5, backgroundColor: color } }; } } /** * @deprecated Use `time/format` instead. * ISO Date format * @param {string} tpl * @param {number} value * @param {boolean} [isUTC=false] Default in local time. * see `module:echarts/scale/Time` * and `module:echarts/util/number#parseDate`. * @inner */ export function formatTime(tpl: string, value: unknown, isUTC?: boolean) { if (__DEV__) { deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format'); } if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year' ) { tpl = 'MM-dd\nyyyy'; } const date = parseDate(value); const getUTC = isUTC ? 'getUTC' : 'get'; const y = (date as any)[getUTC + 'FullYear'](); const M = (date as any)[getUTC + 'Month']() + 1; const d = (date as any)[getUTC + 'Date'](); const h = (date as any)[getUTC + 'Hours'](); const m = (date as any)[getUTC + 'Minutes'](); const s = (date as any)[getUTC + 'Seconds'](); const S = (date as any)[getUTC + 'Milliseconds'](); tpl = tpl.replace('MM', pad(M, 2)) .replace('M', M) .replace('yyyy', y) .replace('yy', pad(y % 100 + '', 2)) .replace('dd', pad(d, 2)) .replace('d', d) .replace('hh', pad(h, 2)) .replace('h', h) .replace('mm', pad(m, 2)) .replace('m', m) .replace('ss', pad(s, 2)) .replace('s', s) .replace('SSS', pad(S, 3)); return tpl; } /** * Capital first * @param {string} str * @return {string} */ export function capitalFirst(str: string): string { return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; } /** * @return Never be null/undefined. */ export function convertToColorString(color: ZRColor, defaultColor?: ColorString): ColorString { defaultColor = defaultColor || 'transparent'; return zrUtil.isString(color) ? color : zrUtil.isObject(color) ? ( (color as GradientObject).colorStops && ((color as GradientObject).colorStops[0] || {}).color || defaultColor ) : defaultColor; } export {truncateText} from 'zrender/src/graphic/helper/parseText'; /** * open new tab * @param link url * @param target blank or self */ export function windowOpen(link: string, target: string): void { /* global window */ if (target === '_blank' || target === 'blank') { const blank = window.open(); blank.opener = null; blank.location.href = link; } else { window.open(link, target); } } export {getTextRect} from '../legacy/getTextRect';