model.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { each, isObject, isArray, createHashMap, map, assert, isString, indexOf, isStringSafe, isNumber } from 'zrender/lib/core/util.js';
  41. import env from 'zrender/lib/core/env.js';
  42. import { isNumeric, getRandomIdBase, getPrecision, round } from './number.js';
  43. import { warn } from './log.js';
  44. function interpolateNumber(p0, p1, percent) {
  45. return (p1 - p0) * percent + p0;
  46. }
  47. /**
  48. * Make the name displayable. But we should
  49. * make sure it is not duplicated with user
  50. * specified name, so use '\0';
  51. */
  52. var DUMMY_COMPONENT_NAME_PREFIX = 'series\0';
  53. var INTERNAL_COMPONENT_ID_PREFIX = '\0_ec_\0';
  54. /**
  55. * If value is not array, then translate it to array.
  56. * @param {*} value
  57. * @return {Array} [value] or value
  58. */
  59. export function normalizeToArray(value) {
  60. return value instanceof Array ? value : value == null ? [] : [value];
  61. }
  62. /**
  63. * Sync default option between normal and emphasis like `position` and `show`
  64. * In case some one will write code like
  65. * label: {
  66. * show: false,
  67. * position: 'outside',
  68. * fontSize: 18
  69. * },
  70. * emphasis: {
  71. * label: { show: true }
  72. * }
  73. */
  74. export function defaultEmphasis(opt, key, subOpts) {
  75. // Caution: performance sensitive.
  76. if (opt) {
  77. opt[key] = opt[key] || {};
  78. opt.emphasis = opt.emphasis || {};
  79. opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal
  80. for (var i = 0, len = subOpts.length; i < len; i++) {
  81. var subOptName = subOpts[i];
  82. if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {
  83. opt.emphasis[key][subOptName] = opt[key][subOptName];
  84. }
  85. }
  86. }
  87. }
  88. export var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
  89. // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
  90. // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  91. // // FIXME: deprecated, check and remove it.
  92. // 'textStyle'
  93. // ]);
  94. /**
  95. * The method does not ensure performance.
  96. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  97. * This helper method retrieves value from data.
  98. */
  99. export function getDataItemValue(dataItem) {
  100. return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;
  101. }
  102. /**
  103. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  104. * This helper method determine if dataItem has extra option besides value
  105. */
  106. export function isDataItemOption(dataItem) {
  107. return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array
  108. // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
  109. }
  110. ;
  111. /**
  112. * Mapping to existings for merge.
  113. *
  114. * Mode "normalMege":
  115. * The mapping result (merge result) will keep the order of the existing
  116. * component, rather than the order of new option. Because we should ensure
  117. * some specified index reference (like xAxisIndex) keep work.
  118. * And in most cases, "merge option" is used to update partial option but not
  119. * be expected to change the order.
  120. *
  121. * Mode "replaceMege":
  122. * (1) Only the id mapped components will be merged.
  123. * (2) Other existing components (except internal components) will be removed.
  124. * (3) Other new options will be used to create new component.
  125. * (4) The index of the existing components will not be modified.
  126. * That means their might be "hole" after the removal.
  127. * The new components are created first at those available index.
  128. *
  129. * Mode "replaceAll":
  130. * This mode try to support that reproduce an echarts instance from another
  131. * echarts instance (via `getOption`) in some simple cases.
  132. * In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`,
  133. * which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is,
  134. * the "hole" in `newCmptOptions` will also be kept.
  135. * On the contrary, other modes try best to eliminate holes.
  136. * PENDING: This is an experimental mode yet.
  137. *
  138. * @return See the comment of <MappingResult>.
  139. */
  140. export function mappingToExists(existings, newCmptOptions, mode) {
  141. var isNormalMergeMode = mode === 'normalMerge';
  142. var isReplaceMergeMode = mode === 'replaceMerge';
  143. var isReplaceAllMode = mode === 'replaceAll';
  144. existings = existings || [];
  145. newCmptOptions = (newCmptOptions || []).slice();
  146. var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.
  147. each(newCmptOptions, function (cmptOption, index) {
  148. if (!isObject(cmptOption)) {
  149. newCmptOptions[index] = null;
  150. return;
  151. }
  152. if (process.env.NODE_ENV !== 'production') {
  153. // There is some legacy case that name is set as `false`.
  154. // But should work normally rather than throw error.
  155. if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {
  156. warnInvalidateIdOrName(cmptOption.id);
  157. }
  158. if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) {
  159. warnInvalidateIdOrName(cmptOption.name);
  160. }
  161. }
  162. });
  163. var result = prepareResult(existings, existingIdIdxMap, mode);
  164. if (isNormalMergeMode || isReplaceMergeMode) {
  165. mappingById(result, existings, existingIdIdxMap, newCmptOptions);
  166. }
  167. if (isNormalMergeMode) {
  168. mappingByName(result, newCmptOptions);
  169. }
  170. if (isNormalMergeMode || isReplaceMergeMode) {
  171. mappingByIndex(result, newCmptOptions, isReplaceMergeMode);
  172. } else if (isReplaceAllMode) {
  173. mappingInReplaceAllMode(result, newCmptOptions);
  174. }
  175. makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the
  176. // forEach will omit those items and result in incorrect result.
  177. return result;
  178. }
  179. function prepareResult(existings, existingIdIdxMap, mode) {
  180. var result = [];
  181. if (mode === 'replaceAll') {
  182. return result;
  183. } // Do not use native `map` to in case that the array `existings`
  184. // contains elided items, which will be omitted.
  185. for (var index = 0; index < existings.length; index++) {
  186. var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.
  187. if (existing && existing.id != null) {
  188. existingIdIdxMap.set(existing.id, index);
  189. } // For non-internal-componnets:
  190. // Mode "normalMerge": all existings kept.
  191. // Mode "replaceMerge": all existing removed unless mapped by id.
  192. // For internal-components:
  193. // go with "replaceMerge" approach in both mode.
  194. result.push({
  195. existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,
  196. newOption: null,
  197. keyInfo: null,
  198. brandNew: null
  199. });
  200. }
  201. return result;
  202. }
  203. function mappingById(result, existings, existingIdIdxMap, newCmptOptions) {
  204. // Mapping by id if specified.
  205. each(newCmptOptions, function (cmptOption, index) {
  206. if (!cmptOption || cmptOption.id == null) {
  207. return;
  208. }
  209. var optionId = makeComparableKey(cmptOption.id);
  210. var existingIdx = existingIdIdxMap.get(optionId);
  211. if (existingIdx != null) {
  212. var resultItem = result[existingIdx];
  213. assert(!resultItem.newOption, 'Duplicated option on id "' + optionId + '".');
  214. resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to
  215. // the existings rather than creating new component model.
  216. resultItem.existing = existings[existingIdx];
  217. newCmptOptions[index] = null;
  218. }
  219. });
  220. }
  221. function mappingByName(result, newCmptOptions) {
  222. // Mapping by name if specified.
  223. each(newCmptOptions, function (cmptOption, index) {
  224. if (!cmptOption || cmptOption.name == null) {
  225. return;
  226. }
  227. for (var i = 0; i < result.length; i++) {
  228. var existing = result[i].existing;
  229. if (!result[i].newOption // Consider name: two map to one.
  230. // Can not match when both ids existing but different.
  231. && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {
  232. result[i].newOption = cmptOption;
  233. newCmptOptions[index] = null;
  234. return;
  235. }
  236. }
  237. });
  238. }
  239. function mappingByIndex(result, newCmptOptions, brandNew) {
  240. each(newCmptOptions, function (cmptOption) {
  241. if (!cmptOption) {
  242. return;
  243. } // Find the first place that not mapped by id and not internal component (consider the "hole").
  244. var resultItem;
  245. var nextIdx = 0;
  246. while ( // Be `!resultItem` only when `nextIdx >= result.length`.
  247. (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because
  248. // after mapping performed, model will always be assigned with an id if user not given.
  249. // After that all models have id.
  250. // (2) If new option has id, it can only set to a hole or append to the last. It should
  251. // not be merged to the existings with different id. Because id should not be overwritten.
  252. // (3) Name can be overwritten, because axis use name as 'show label text'.
  253. resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode "replaceMerge", here no not-mapped-non-internal-existing.
  254. resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {
  255. nextIdx++;
  256. }
  257. if (resultItem) {
  258. resultItem.newOption = cmptOption;
  259. resultItem.brandNew = brandNew;
  260. } else {
  261. result.push({
  262. newOption: cmptOption,
  263. brandNew: brandNew,
  264. existing: null,
  265. keyInfo: null
  266. });
  267. }
  268. nextIdx++;
  269. });
  270. }
  271. function mappingInReplaceAllMode(result, newCmptOptions) {
  272. each(newCmptOptions, function (cmptOption) {
  273. // The feature "reproduce" requires "hole" will also reproduced
  274. // in case that component index referring are broken.
  275. result.push({
  276. newOption: cmptOption,
  277. brandNew: true,
  278. existing: null,
  279. keyInfo: null
  280. });
  281. });
  282. }
  283. /**
  284. * Make id and name for mapping result (result of mappingToExists)
  285. * into `keyInfo` field.
  286. */
  287. function makeIdAndName(mapResult) {
  288. // We use this id to hash component models and view instances
  289. // in echarts. id can be specified by user, or auto generated.
  290. // The id generation rule ensures new view instance are able
  291. // to mapped to old instance when setOption are called in
  292. // no-merge mode. So we generate model id by name and plus
  293. // type in view id.
  294. // name can be duplicated among components, which is convenient
  295. // to specify multi components (like series) by one name.
  296. // Ensure that each id is distinct.
  297. var idMap = createHashMap();
  298. each(mapResult, function (item) {
  299. var existing = item.existing;
  300. existing && idMap.set(existing.id, item);
  301. });
  302. each(mapResult, function (item) {
  303. var opt = item.newOption; // Force ensure id not duplicated.
  304. assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));
  305. opt && opt.id != null && idMap.set(opt.id, item);
  306. !item.keyInfo && (item.keyInfo = {});
  307. }); // Make name and id.
  308. each(mapResult, function (item, index) {
  309. var existing = item.existing;
  310. var opt = item.newOption;
  311. var keyInfo = item.keyInfo;
  312. if (!isObject(opt)) {
  313. return;
  314. } // Name can be overwritten. Consider case: axis.name = '20km'.
  315. // But id generated by name will not be changed, which affect
  316. // only in that case: setOption with 'not merge mode' and view
  317. // instance will be recreated, which can be accepted.
  318. keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name,
  319. // because name may be used like in color pallet.
  320. : DUMMY_COMPONENT_NAME_PREFIX + index;
  321. if (existing) {
  322. keyInfo.id = makeComparableKey(existing.id);
  323. } else if (opt.id != null) {
  324. keyInfo.id = makeComparableKey(opt.id);
  325. } else {
  326. // Consider this situatoin:
  327. // optionA: [{name: 'a'}, {name: 'a'}, {..}]
  328. // optionB [{..}, {name: 'a'}, {name: 'a'}]
  329. // Series with the same name between optionA and optionB
  330. // should be mapped.
  331. var idNum = 0;
  332. do {
  333. keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
  334. } while (idMap.get(keyInfo.id));
  335. }
  336. idMap.set(keyInfo.id, item);
  337. });
  338. }
  339. function keyExistAndEqual(attr, obj1, obj2) {
  340. var key1 = convertOptionIdName(obj1[attr], null);
  341. var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.
  342. return key1 != null && key2 != null && key1 === key2;
  343. }
  344. /**
  345. * @return return null if not exist.
  346. */
  347. function makeComparableKey(val) {
  348. if (process.env.NODE_ENV !== 'production') {
  349. if (val == null) {
  350. throw new Error();
  351. }
  352. }
  353. return convertOptionIdName(val, '');
  354. }
  355. export function convertOptionIdName(idOrName, defaultValue) {
  356. if (idOrName == null) {
  357. return defaultValue;
  358. }
  359. return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue;
  360. }
  361. function warnInvalidateIdOrName(idOrName) {
  362. if (process.env.NODE_ENV !== 'production') {
  363. warn('`' + idOrName + '` is invalid id or name. Must be a string or number.');
  364. }
  365. }
  366. function isValidIdOrName(idOrName) {
  367. return isStringSafe(idOrName) || isNumeric(idOrName);
  368. }
  369. export function isNameSpecified(componentModel) {
  370. var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.
  371. return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));
  372. }
  373. /**
  374. * @public
  375. * @param {Object} cmptOption
  376. * @return {boolean}
  377. */
  378. export function isComponentIdInternal(cmptOption) {
  379. return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;
  380. }
  381. export function makeInternalComponentId(idSuffix) {
  382. return INTERNAL_COMPONENT_ID_PREFIX + idSuffix;
  383. }
  384. export function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {
  385. // Set mainType and complete subType.
  386. each(mappingResult, function (item) {
  387. var newOption = item.newOption;
  388. if (isObject(newOption)) {
  389. item.keyInfo.mainType = mainType;
  390. item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor);
  391. }
  392. });
  393. }
  394. function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {
  395. var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.
  396. : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType
  397. return subType;
  398. }
  399. /**
  400. * A helper for removing duplicate items between batchA and batchB,
  401. * and in themselves, and categorize by series.
  402. *
  403. * @param batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  404. * @param batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  405. * @return result: [resultBatchA, resultBatchB]
  406. */
  407. export function compressBatches(batchA, batchB) {
  408. var mapA = {};
  409. var mapB = {};
  410. makeMap(batchA || [], mapA);
  411. makeMap(batchB || [], mapB, mapA);
  412. return [mapToArray(mapA), mapToArray(mapB)];
  413. function makeMap(sourceBatch, map, otherMap) {
  414. for (var i = 0, len = sourceBatch.length; i < len; i++) {
  415. var seriesId = convertOptionIdName(sourceBatch[i].seriesId, null);
  416. if (seriesId == null) {
  417. return;
  418. }
  419. var dataIndices = normalizeToArray(sourceBatch[i].dataIndex);
  420. var otherDataIndices = otherMap && otherMap[seriesId];
  421. for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {
  422. var dataIndex = dataIndices[j];
  423. if (otherDataIndices && otherDataIndices[dataIndex]) {
  424. otherDataIndices[dataIndex] = null;
  425. } else {
  426. (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1;
  427. }
  428. }
  429. }
  430. }
  431. function mapToArray(map, isData) {
  432. var result = [];
  433. for (var i in map) {
  434. if (map.hasOwnProperty(i) && map[i] != null) {
  435. if (isData) {
  436. result.push(+i);
  437. } else {
  438. var dataIndices = mapToArray(map[i], true);
  439. dataIndices.length && result.push({
  440. seriesId: i,
  441. dataIndex: dataIndices
  442. });
  443. }
  444. }
  445. }
  446. return result;
  447. }
  448. }
  449. /**
  450. * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name
  451. * each of which can be Array or primary type.
  452. * @return dataIndex If not found, return undefined/null.
  453. */
  454. export function queryDataIndex(data, payload) {
  455. if (payload.dataIndexInside != null) {
  456. return payload.dataIndexInside;
  457. } else if (payload.dataIndex != null) {
  458. return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {
  459. return data.indexOfRawIndex(value);
  460. }) : data.indexOfRawIndex(payload.dataIndex);
  461. } else if (payload.name != null) {
  462. return isArray(payload.name) ? map(payload.name, function (value) {
  463. return data.indexOfName(value);
  464. }) : data.indexOfName(payload.name);
  465. }
  466. }
  467. /**
  468. * Enable property storage to any host object.
  469. * Notice: Serialization is not supported.
  470. *
  471. * For example:
  472. * let inner = zrUitl.makeInner();
  473. *
  474. * function some1(hostObj) {
  475. * inner(hostObj).someProperty = 1212;
  476. * ...
  477. * }
  478. * function some2() {
  479. * let fields = inner(this);
  480. * fields.someProperty1 = 1212;
  481. * fields.someProperty2 = 'xx';
  482. * ...
  483. * }
  484. *
  485. * @return {Function}
  486. */
  487. export function makeInner() {
  488. var key = '__ec_inner_' + innerUniqueIndex++;
  489. return function (hostObj) {
  490. return hostObj[key] || (hostObj[key] = {});
  491. };
  492. }
  493. var innerUniqueIndex = getRandomIdBase();
  494. /**
  495. * The same behavior as `component.getReferringComponents`.
  496. */
  497. export function parseFinder(ecModel, finderInput, opt) {
  498. var _a = preParseFinder(finderInput, opt),
  499. mainTypeSpecified = _a.mainTypeSpecified,
  500. queryOptionMap = _a.queryOptionMap,
  501. others = _a.others;
  502. var result = others;
  503. var defaultMainType = opt ? opt.defaultMainType : null;
  504. if (!mainTypeSpecified && defaultMainType) {
  505. queryOptionMap.set(defaultMainType, {});
  506. }
  507. queryOptionMap.each(function (queryOption, mainType) {
  508. var queryResult = queryReferringComponents(ecModel, mainType, queryOption, {
  509. useDefault: defaultMainType === mainType,
  510. enableAll: opt && opt.enableAll != null ? opt.enableAll : true,
  511. enableNone: opt && opt.enableNone != null ? opt.enableNone : true
  512. });
  513. result[mainType + 'Models'] = queryResult.models;
  514. result[mainType + 'Model'] = queryResult.models[0];
  515. });
  516. return result;
  517. }
  518. export function preParseFinder(finderInput, opt) {
  519. var finder;
  520. if (isString(finderInput)) {
  521. var obj = {};
  522. obj[finderInput + 'Index'] = 0;
  523. finder = obj;
  524. } else {
  525. finder = finderInput;
  526. }
  527. var queryOptionMap = createHashMap();
  528. var others = {};
  529. var mainTypeSpecified = false;
  530. each(finder, function (value, key) {
  531. // Exclude 'dataIndex' and other illgal keys.
  532. if (key === 'dataIndex' || key === 'dataIndexInside') {
  533. others[key] = value;
  534. return;
  535. }
  536. var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
  537. var mainType = parsedKey[1];
  538. var queryType = (parsedKey[2] || '').toLowerCase();
  539. if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {
  540. return;
  541. }
  542. mainTypeSpecified = mainTypeSpecified || !!mainType;
  543. var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {});
  544. queryOption[queryType] = value;
  545. });
  546. return {
  547. mainTypeSpecified: mainTypeSpecified,
  548. queryOptionMap: queryOptionMap,
  549. others: others
  550. };
  551. }
  552. export var SINGLE_REFERRING = {
  553. useDefault: true,
  554. enableAll: false,
  555. enableNone: false
  556. };
  557. export var MULTIPLE_REFERRING = {
  558. useDefault: false,
  559. enableAll: true,
  560. enableNone: true
  561. };
  562. export function queryReferringComponents(ecModel, mainType, userOption, opt) {
  563. opt = opt || SINGLE_REFERRING;
  564. var indexOption = userOption.index;
  565. var idOption = userOption.id;
  566. var nameOption = userOption.name;
  567. var result = {
  568. models: null,
  569. specified: indexOption != null || idOption != null || nameOption != null
  570. };
  571. if (!result.specified) {
  572. // Use the first as default if `useDefault`.
  573. var firstCmpt = void 0;
  574. result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];
  575. return result;
  576. }
  577. if (indexOption === 'none' || indexOption === false) {
  578. assert(opt.enableNone, '`"none"` or `false` is not a valid value on index option.');
  579. result.models = [];
  580. return result;
  581. } // `queryComponents` will return all components if
  582. // both all of index/id/name are null/undefined.
  583. if (indexOption === 'all') {
  584. assert(opt.enableAll, '`"all"` is not a valid value on index option.');
  585. indexOption = idOption = nameOption = null;
  586. }
  587. result.models = ecModel.queryComponents({
  588. mainType: mainType,
  589. index: indexOption,
  590. id: idOption,
  591. name: nameOption
  592. });
  593. return result;
  594. }
  595. export function setAttribute(dom, key, value) {
  596. dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;
  597. }
  598. export function getAttribute(dom, key) {
  599. return dom.getAttribute ? dom.getAttribute(key) : dom[key];
  600. }
  601. export function getTooltipRenderMode(renderModeOption) {
  602. if (renderModeOption === 'auto') {
  603. // Using html when `document` exists, use richText otherwise
  604. return env.domSupported ? 'html' : 'richText';
  605. } else {
  606. return renderModeOption || 'html';
  607. }
  608. }
  609. /**
  610. * Group a list by key.
  611. */
  612. export function groupData(array, getKey // return key
  613. ) {
  614. var buckets = createHashMap();
  615. var keys = [];
  616. each(array, function (item) {
  617. var key = getKey(item);
  618. (buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item);
  619. });
  620. return {
  621. keys: keys,
  622. buckets: buckets
  623. };
  624. }
  625. /**
  626. * Interpolate raw values of a series with percent
  627. *
  628. * @param data data
  629. * @param labelModel label model of the text element
  630. * @param sourceValue start value. May be null/undefined when init.
  631. * @param targetValue end value
  632. * @param percent 0~1 percentage; 0 uses start value while 1 uses end value
  633. * @return interpolated values
  634. * If `sourceValue` and `targetValue` are `number`, return `number`.
  635. * If `sourceValue` and `targetValue` are `string`, return `string`.
  636. * If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.
  637. * Other cases do not supported.
  638. */
  639. export function interpolateRawValues(data, precision, sourceValue, targetValue, percent) {
  640. var isAutoPrecision = precision == null || precision === 'auto';
  641. if (targetValue == null) {
  642. return targetValue;
  643. }
  644. if (isNumber(targetValue)) {
  645. var value = interpolateNumber(sourceValue || 0, targetValue, percent);
  646. return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision);
  647. } else if (isString(targetValue)) {
  648. return percent < 1 ? sourceValue : targetValue;
  649. } else {
  650. var interpolated = [];
  651. var leftArr = sourceValue;
  652. var rightArr = targetValue;
  653. var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);
  654. for (var i = 0; i < length_1; ++i) {
  655. var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims
  656. if (info && info.type === 'ordinal') {
  657. // In init, there is no `sourceValue`, but should better not to get undefined result.
  658. interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];
  659. } else {
  660. var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;
  661. var rightVal = rightArr[i];
  662. var value = interpolateNumber(leftVal, rightVal, percent);
  663. interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision);
  664. }
  665. }
  666. return interpolated;
  667. }
  668. }