DataView.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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 { __extends } from "tslib";
  41. /* global document */
  42. import * as echarts from '../../../core/echarts.js';
  43. import * as zrUtil from 'zrender/lib/core/util.js';
  44. import { ToolboxFeature } from '../featureManager.js';
  45. import { addEventListener } from 'zrender/lib/core/event.js';
  46. import { warn } from '../../../util/log.js';
  47. import tokens from '../../../visual/tokens.js';
  48. /* global document */
  49. var BLOCK_SPLITER = new Array(60).join('-');
  50. var ITEM_SPLITER = '\t';
  51. /**
  52. * Group series into two types
  53. * 1. on category axis, like line, bar
  54. * 2. others, like scatter, pie
  55. */
  56. function groupSeries(ecModel) {
  57. var seriesGroupByCategoryAxis = {};
  58. var otherSeries = [];
  59. var meta = [];
  60. ecModel.eachRawSeries(function (seriesModel) {
  61. var coordSys = seriesModel.coordinateSystem;
  62. if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {
  63. // TODO: TYPE Consider polar? Include polar may increase unecessary bundle size.
  64. var baseAxis = coordSys.getBaseAxis();
  65. if (baseAxis.type === 'category') {
  66. var key = baseAxis.dim + '_' + baseAxis.index;
  67. if (!seriesGroupByCategoryAxis[key]) {
  68. seriesGroupByCategoryAxis[key] = {
  69. categoryAxis: baseAxis,
  70. valueAxis: coordSys.getOtherAxis(baseAxis),
  71. series: []
  72. };
  73. meta.push({
  74. axisDim: baseAxis.dim,
  75. axisIndex: baseAxis.index
  76. });
  77. }
  78. seriesGroupByCategoryAxis[key].series.push(seriesModel);
  79. } else {
  80. otherSeries.push(seriesModel);
  81. }
  82. } else {
  83. otherSeries.push(seriesModel);
  84. }
  85. });
  86. return {
  87. seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,
  88. other: otherSeries,
  89. meta: meta
  90. };
  91. }
  92. /**
  93. * Assemble content of series on cateogory axis
  94. * @inner
  95. */
  96. function assembleSeriesWithCategoryAxis(groups) {
  97. var tables = [];
  98. zrUtil.each(groups, function (group, key) {
  99. var categoryAxis = group.categoryAxis;
  100. var valueAxis = group.valueAxis;
  101. var valueAxisDim = valueAxis.dim;
  102. var headers = [' '].concat(zrUtil.map(group.series, function (series) {
  103. return series.name;
  104. }));
  105. // @ts-ignore TODO Polar
  106. var columns = [categoryAxis.model.getCategories()];
  107. zrUtil.each(group.series, function (series) {
  108. var rawData = series.getRawData();
  109. columns.push(series.getRawData().mapArray(rawData.mapDimension(valueAxisDim), function (val) {
  110. return val;
  111. }));
  112. });
  113. // Assemble table content
  114. var lines = [headers.join(ITEM_SPLITER)];
  115. for (var i = 0; i < columns[0].length; i++) {
  116. var items = [];
  117. for (var j = 0; j < columns.length; j++) {
  118. items.push(columns[j][i]);
  119. }
  120. lines.push(items.join(ITEM_SPLITER));
  121. }
  122. tables.push(lines.join('\n'));
  123. });
  124. return tables.join('\n\n' + BLOCK_SPLITER + '\n\n');
  125. }
  126. /**
  127. * Assemble content of other series
  128. */
  129. function assembleOtherSeries(series) {
  130. return zrUtil.map(series, function (series) {
  131. var data = series.getRawData();
  132. var lines = [series.name];
  133. var vals = [];
  134. data.each(data.dimensions, function () {
  135. var argLen = arguments.length;
  136. var dataIndex = arguments[argLen - 1];
  137. var name = data.getName(dataIndex);
  138. for (var i = 0; i < argLen - 1; i++) {
  139. vals[i] = arguments[i];
  140. }
  141. lines.push((name ? name + ITEM_SPLITER : '') + vals.join(ITEM_SPLITER));
  142. });
  143. return lines.join('\n');
  144. }).join('\n\n' + BLOCK_SPLITER + '\n\n');
  145. }
  146. function getContentFromModel(ecModel) {
  147. var result = groupSeries(ecModel);
  148. return {
  149. value: zrUtil.filter([assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), assembleOtherSeries(result.other)], function (str) {
  150. return !!str.replace(/[\n\t\s]/g, '');
  151. }).join('\n\n' + BLOCK_SPLITER + '\n\n'),
  152. meta: result.meta
  153. };
  154. }
  155. function trim(str) {
  156. return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  157. }
  158. /**
  159. * If a block is tsv format
  160. */
  161. function isTSVFormat(block) {
  162. // Simple method to find out if a block is tsv format
  163. var firstLine = block.slice(0, block.indexOf('\n'));
  164. if (firstLine.indexOf(ITEM_SPLITER) >= 0) {
  165. return true;
  166. }
  167. }
  168. var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');
  169. /**
  170. * @param {string} tsv
  171. * @return {Object}
  172. */
  173. function parseTSVContents(tsv) {
  174. var tsvLines = tsv.split(/\n+/g);
  175. var headers = trim(tsvLines.shift()).split(itemSplitRegex);
  176. var categories = [];
  177. var series = zrUtil.map(headers, function (header) {
  178. return {
  179. name: header,
  180. data: []
  181. };
  182. });
  183. for (var i = 0; i < tsvLines.length; i++) {
  184. var items = trim(tsvLines[i]).split(itemSplitRegex);
  185. categories.push(items.shift());
  186. for (var j = 0; j < items.length; j++) {
  187. series[j] && (series[j].data[i] = items[j]);
  188. }
  189. }
  190. return {
  191. series: series,
  192. categories: categories
  193. };
  194. }
  195. function parseListContents(str) {
  196. var lines = str.split(/\n+/g);
  197. var seriesName = trim(lines.shift());
  198. var data = [];
  199. for (var i = 0; i < lines.length; i++) {
  200. // if line is empty, ignore it.
  201. // there is a case that a user forgot to delete `\n`.
  202. var line = trim(lines[i]);
  203. if (!line) {
  204. continue;
  205. }
  206. var items = line.split(itemSplitRegex);
  207. var name_1 = '';
  208. var value = void 0;
  209. var hasName = false;
  210. if (isNaN(items[0])) {
  211. // First item is name
  212. hasName = true;
  213. name_1 = items[0];
  214. items = items.slice(1);
  215. data[i] = {
  216. name: name_1,
  217. value: []
  218. };
  219. value = data[i].value;
  220. } else {
  221. value = data[i] = [];
  222. }
  223. for (var j = 0; j < items.length; j++) {
  224. value.push(+items[j]);
  225. }
  226. if (value.length === 1) {
  227. hasName ? data[i].value = value[0] : data[i] = value[0];
  228. }
  229. }
  230. return {
  231. name: seriesName,
  232. data: data
  233. };
  234. }
  235. function parseContents(str, blockMetaList) {
  236. var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g'));
  237. var newOption = {
  238. series: []
  239. };
  240. zrUtil.each(blocks, function (block, idx) {
  241. if (isTSVFormat(block)) {
  242. var result = parseTSVContents(block);
  243. var blockMeta = blockMetaList[idx];
  244. var axisKey = blockMeta.axisDim + 'Axis';
  245. if (blockMeta) {
  246. newOption[axisKey] = newOption[axisKey] || [];
  247. newOption[axisKey][blockMeta.axisIndex] = {
  248. data: result.categories
  249. };
  250. newOption.series = newOption.series.concat(result.series);
  251. }
  252. } else {
  253. var result = parseListContents(block);
  254. newOption.series.push(result);
  255. }
  256. });
  257. return newOption;
  258. }
  259. var DataView = /** @class */function (_super) {
  260. __extends(DataView, _super);
  261. function DataView() {
  262. return _super !== null && _super.apply(this, arguments) || this;
  263. }
  264. DataView.prototype.onclick = function (ecModel, api) {
  265. // FIXME: better way?
  266. setTimeout(function () {
  267. api.dispatchAction({
  268. type: 'hideTip'
  269. });
  270. });
  271. var container = api.getDom();
  272. var model = this.model;
  273. if (this._dom) {
  274. container.removeChild(this._dom);
  275. }
  276. var root = document.createElement('div');
  277. // use padding to avoid 5px whitespace
  278. root.style.cssText = 'position:absolute;top:0;bottom:0;left:0;right:0;padding:5px';
  279. root.style.backgroundColor = model.get('backgroundColor') || tokens.color.neutral00;
  280. // Create elements
  281. var header = document.createElement('h4');
  282. var lang = model.get('lang') || [];
  283. header.innerHTML = lang[0] || model.get('title');
  284. header.style.cssText = 'margin:10px 20px';
  285. header.style.color = model.get('textColor');
  286. var viewMain = document.createElement('div');
  287. var textarea = document.createElement('textarea');
  288. viewMain.style.cssText = 'overflow:auto';
  289. var optionToContent = model.get('optionToContent');
  290. var contentToOption = model.get('contentToOption');
  291. var result = getContentFromModel(ecModel);
  292. if (zrUtil.isFunction(optionToContent)) {
  293. var htmlOrDom = optionToContent(api.getOption());
  294. if (zrUtil.isString(htmlOrDom)) {
  295. viewMain.innerHTML = htmlOrDom;
  296. } else if (zrUtil.isDom(htmlOrDom)) {
  297. viewMain.appendChild(htmlOrDom);
  298. }
  299. } else {
  300. // Use default textarea
  301. textarea.readOnly = model.get('readOnly');
  302. var style = textarea.style;
  303. // eslint-disable-next-line max-len
  304. style.cssText = 'display:block;width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;resize:none;box-sizing:border-box;outline:none';
  305. style.color = model.get('textColor');
  306. style.borderColor = model.get('textareaBorderColor');
  307. style.backgroundColor = model.get('textareaColor');
  308. textarea.value = result.value;
  309. viewMain.appendChild(textarea);
  310. }
  311. var blockMetaList = result.meta;
  312. var buttonContainer = document.createElement('div');
  313. buttonContainer.style.cssText = 'position:absolute;bottom:5px;left:0;right:0';
  314. // eslint-disable-next-line max-len
  315. var buttonStyle = 'float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';
  316. var closeButton = document.createElement('div');
  317. var refreshButton = document.createElement('div');
  318. buttonStyle += ';background-color:' + model.get('buttonColor');
  319. buttonStyle += ';color:' + model.get('buttonTextColor');
  320. var self = this;
  321. function close() {
  322. container.removeChild(root);
  323. self._dom = null;
  324. }
  325. addEventListener(closeButton, 'click', close);
  326. addEventListener(refreshButton, 'click', function () {
  327. if (contentToOption == null && optionToContent != null || contentToOption != null && optionToContent == null) {
  328. if (process.env.NODE_ENV !== 'production') {
  329. // eslint-disable-next-line
  330. warn('It seems you have just provided one of `contentToOption` and `optionToContent` functions but missed the other one. Data change is ignored.');
  331. }
  332. close();
  333. return;
  334. }
  335. var newOption;
  336. try {
  337. if (zrUtil.isFunction(contentToOption)) {
  338. newOption = contentToOption(viewMain, api.getOption());
  339. } else {
  340. newOption = parseContents(textarea.value, blockMetaList);
  341. }
  342. } catch (e) {
  343. close();
  344. throw new Error('Data view format error ' + e);
  345. }
  346. if (newOption) {
  347. api.dispatchAction({
  348. type: 'changeDataView',
  349. newOption: newOption
  350. });
  351. }
  352. close();
  353. });
  354. closeButton.innerHTML = lang[1];
  355. refreshButton.innerHTML = lang[2];
  356. refreshButton.style.cssText = closeButton.style.cssText = buttonStyle;
  357. !model.get('readOnly') && buttonContainer.appendChild(refreshButton);
  358. buttonContainer.appendChild(closeButton);
  359. root.appendChild(header);
  360. root.appendChild(viewMain);
  361. root.appendChild(buttonContainer);
  362. viewMain.style.height = container.clientHeight - 80 + 'px';
  363. container.appendChild(root);
  364. this._dom = root;
  365. };
  366. DataView.prototype.remove = function (ecModel, api) {
  367. this._dom && api.getDom().removeChild(this._dom);
  368. };
  369. DataView.prototype.dispose = function (ecModel, api) {
  370. this.remove(ecModel, api);
  371. };
  372. DataView.getDefaultOption = function (ecModel) {
  373. var defaultOption = {
  374. show: true,
  375. readOnly: false,
  376. optionToContent: null,
  377. contentToOption: null,
  378. // eslint-disable-next-line
  379. icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',
  380. title: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'title']),
  381. lang: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'lang']),
  382. backgroundColor: tokens.color.background,
  383. textColor: tokens.color.primary,
  384. textareaColor: tokens.color.background,
  385. textareaBorderColor: tokens.color.border,
  386. buttonColor: tokens.color.accent50,
  387. buttonTextColor: tokens.color.neutral00
  388. };
  389. return defaultOption;
  390. };
  391. return DataView;
  392. }(ToolboxFeature);
  393. /**
  394. * @inner
  395. */
  396. function tryMergeDataOption(newData, originalData) {
  397. return zrUtil.map(newData, function (newVal, idx) {
  398. var original = originalData && originalData[idx];
  399. if (zrUtil.isObject(original) && !zrUtil.isArray(original)) {
  400. var newValIsObject = zrUtil.isObject(newVal) && !zrUtil.isArray(newVal);
  401. if (!newValIsObject) {
  402. newVal = {
  403. value: newVal
  404. };
  405. }
  406. // original data has name but new data has no name
  407. var shouldDeleteName = original.name != null && newVal.name == null;
  408. // Original data has option
  409. newVal = zrUtil.defaults(newVal, original);
  410. shouldDeleteName && delete newVal.name;
  411. return newVal;
  412. } else {
  413. return newVal;
  414. }
  415. });
  416. }
  417. // TODO: SELF REGISTERED.
  418. echarts.registerAction({
  419. type: 'changeDataView',
  420. event: 'dataViewChanged',
  421. update: 'prepareAndUpdate'
  422. }, function (payload, ecModel) {
  423. var newSeriesOptList = [];
  424. zrUtil.each(payload.newOption.series, function (seriesOpt) {
  425. var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];
  426. if (!seriesModel) {
  427. // New created series
  428. // Geuss the series type
  429. newSeriesOptList.push(zrUtil.extend({
  430. // Default is scatter
  431. type: 'scatter'
  432. }, seriesOpt));
  433. } else {
  434. var originalData = seriesModel.get('data');
  435. newSeriesOptList.push({
  436. name: seriesOpt.name,
  437. data: tryMergeDataOption(seriesOpt.data, originalData)
  438. });
  439. }
  440. });
  441. ecModel.mergeOption(zrUtil.defaults({
  442. series: newSeriesOptList
  443. }, payload.newOption));
  444. });
  445. export default DataView;