/*
|
* 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 { 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;
|
|
|
const replaceReg = /([&<>"'])/g;
|
const replaceMap: Dictionary<string> = {
|
'&': '&',
|
'<': '<',
|
'>': '>',
|
'"': '"',
|
'\'': '''
|
};
|
|
export function encodeHTML(source: string): string {
|
return source == null
|
? ''
|
: (source + '').replace(replaceReg, function (str, c) {
|
return replaceMap[c];
|
});
|
}
|
|
|
/**
|
* 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)
|
: '-';
|
}
|
|
|
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<any> {
|
// Param name list for mapping `a`, `b`, `c`, `d`, `e`
|
$vars: string[];
|
}
|
/**
|
* Template formatter
|
* @param {Array.<Object>|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<any>, 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<unknown>;
|
}
|
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'
|
? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;'
|
+ 'border-radius:4px;width:4px;height:4px;background-color:'
|
// Only support string
|
+ encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'
|
: '<span style="display:inline-block;margin-right:4px;'
|
+ 'border-radius:10px;width:10px;height:10px;background-color:'
|
+ encodeHTML(color) + ';' + (extraCssText || '') + '"></span>';
|
}
|
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 repeatly 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 utc = isUTC ? 'UTC' : '';
|
const y = (date as any)['get' + utc + 'FullYear']();
|
const M = (date as any)['get' + utc + 'Month']() + 1;
|
const d = (date as any)['get' + utc + 'Date']();
|
const h = (date as any)['get' + utc + 'Hours']();
|
const m = (date as any)['get' + utc + 'Minutes']();
|
const s = (date as any)['get' + utc + 'Seconds']();
|
const S = (date as any)['get' + utc + 'Milliseconds']();
|
|
tpl = tpl.replace('MM', pad(M, 2))
|
.replace('M', M)
|
.replace('yyyy', y)
|
.replace('yy', y % 100 + '')
|
.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';
|