-
Notifications
You must be signed in to change notification settings - Fork 0
/
vega-utils.js
194 lines (171 loc) · 5.78 KB
/
vega-utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
'use strict';
const lzString = require('lz-string');
const VEGA_EDITOR_BASE_URL = 'https://vega.github.io/editor/#/url/';
const VEGA_SCHEMA_BASE_URL = 'https://vega.github.io/schema/';
const VEGA_DATA_BASE_URL = 'https://vega.github.io/vega-datasets/';
const VEGA_UNFURL_BASE_URL = 'https://vega-unfurl.glitch.me/';
const VEGA_DOCUMENT_TYPES = ['vg.json'];
/**
* Converts shared Slack link to an unfurl object
* for custom vega unfurl message attachment.
* @param {string} link The Vega link to unfurl.
*/
function getLinkInfo(link) {
// create initial unfurl link info
const linkInfo = {
color: "#36a64f",
title: link.url,
title_link: link.url,
url: link.url
};
if (link.url.startsWith(VEGA_EDITOR_BASE_URL)) {
// extract vega spec info from url
const vegaSpecInfo = getVegaSpecInfo(VEGA_EDITOR_BASE_URL, link.url);
const vegaSpec = vegaSpecInfo.spec;
// extract vega spec title, description and json schema info
const title = vegaSpec['title'];
const description = vegaSpec['description'];
const jsonSchemaUrl = vegaSpec['$schema'];
// add preview image
linkInfo['image_url'] = `${VEGA_UNFURL_BASE_URL}svg/${vegaSpecInfo.type}/${vegaSpecInfo.compressedString}`;
// add title
if (title !== undefined) {
linkInfo['title'] = `${title}.${vegaSpecInfo.fileType}`;
}
else if (description !== undefined) {
// use description for link title
linkInfo['title'] = `${description.substr(0, 100)}.${vegaSpecInfo.fileType}`;
} else {
linkInfo['title'] = `Unititled.${vegaSpecInfo.fileType}`;
}
// add description
if (description !== undefined) {
linkInfo['text'] = description;
}
// add json schema info
const fields = [];
if (jsonSchemaUrl !== undefined) {
const vegaSchemaTitle = jsonSchemaUrl.replace(VEGA_SCHEMA_BASE_URL, '').replace('/', ' | ').replace('.json', '')
fields.push({
title: 'schema',
value: `:small_blue_diamond: <${jsonSchemaUrl}|${vegaSchemaTitle}>`
});
}
// add data links
const dataLinks = getDataLinks(vegaSpec);
if (dataLinks.length > 0) {
fields.push({
title: 'data',
value: dataLinks.map(link => `:small_blue_diamond: <${link}|${link}>`).join('\n')
});
}
// create view/save links
let viewLinks = VEGA_DOCUMENT_TYPES.map(type => {
return `<${VEGA_UNFURL_BASE_URL}${type}/${vegaSpecInfo.type}/${vegaSpecInfo.compressedString}|${type}>`;
}).join(' | ');
if (vegaSpecInfo.type === 'vega-lite') {
// add vl.json link
viewLinks += ` | <${VEGA_UNFURL_BASE_URL}vl.json/vega-lite/${vegaSpecInfo.compressedString}|vl.json>`;
}
// add view/save links
fields.push({
title: 'view/save',
value: viewLinks
});
if (fields.length > 0) {
// add fields
linkInfo.fields = fields;
}
}
return linkInfo;
} // end of getLinkInfo()
/**
* Creates Vega spec info from encoded vega spec url.
* @param {string} baseUrl Vega spec base url to strip out.
* @param {*} vegaSpecUrl Full Vega spec url.
*/
function getVegaSpecInfo(baseUrl, vegaSpecUrl) {
// extract vega spec from url
const vegaSpecUrlPart = vegaSpecUrl.replace(baseUrl, '');
const vegaSpecPosition = vegaSpecUrlPart.indexOf('/');
const vegaSpecType = vegaSpecUrlPart.substring(0, vegaSpecPosition);
console.log(`\tspec type: ${vegaSpecType}`);
const compressedVegaSpec = vegaSpecUrlPart.substring(vegaSpecPosition + 1);
const vegaSpecString = lzString.decompressFromEncodedURIComponent(compressedVegaSpec);
const vegaSpec = JSON.parse(vegaSpecString);
// console.log(vegaSpecString);
return {
type: vegaSpecType,
fileType: (vegaSpecType === 'vega' ? 'vg.json' : 'vl.json'),
spec: vegaSpec,
specString: vegaSpecString,
compressedString: compressedVegaSpec
};
}
/**
* Extracts data urls from Vega spec.
* @param spec Vega spec document.
*/
function getDataLinks(spec) {
// get top level data urls
let dataUrls = getDataUrls(spec);
// add nested spec data urls for view compositions (facets, repeats, etc.)
dataUrls = dataUrls.concat(getDataUrls(spec['spec']));
console.log('dataUrls:', dataUrls);
// create data links to attach
return dataUrls.map(dataUrl => {
if (dataUrl.startsWith('http://') || dataUrl.startsWith('https://')) {
// add remote data source reference
return dataUrl;
}
else {
// convert relative data urls to vega data sets references for built-in examples
return (VEGA_DATA_BASE_URL + dataUrl);
}
});
}
/**
* Recursively extracts data urls from the specified vega spec document.
* @param spec Vega spec document root or nested vega data container.
*/
function getDataUrls(spec) {
let dataUrls = [];
if (spec === undefined){
return dataUrls; // base case
}
const data = spec['data'];
const transforms = spec['transform'];
let layers = [];
layers = layers.concat(spec['layer']);
layers = layers.concat(spec['concat']);
layers = layers.concat(spec['hconcat']);
layers = layers.concat(spec['vconcat']);
if (data !== undefined) {
// get top level data references
if (Array.isArray(data)) {
data.filter(d => d['url'] !== undefined).forEach(d => {
dataUrls.push(d['url']);
});
}
else if (data['url'] !== undefined) {
dataUrls.push(data['url']);
}
}
if (layers !== undefined && Array.isArray(layers)) {
// get layers data references
layers.forEach(layer => {
dataUrls = dataUrls.concat(getDataUrls(layer));
});
}
if (transforms !== undefined) {
// get transform data references
transforms.forEach(transformData => {
dataUrls = dataUrls.concat(getDataUrls(transformData['from']));
});
}
return dataUrls;
}
module.exports = {
getLinkInfo: getLinkInfo,
getVegaSpecInfo: getVegaSpecInfo
};