model.js 26 KB

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