model.js 26 KB

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