@flourish/sdk 3.15.1 → 3.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RELEASE_NOTES.md +12 -0
- package/lib/cmd/assign-version-number.js +1 -1
- package/lib/cmd/help.js +1 -1
- package/lib/cmd/publish.js +34 -13
- package/lib/cmd/run.js +2 -1
- package/lib/cmd/version.js +1 -1
- package/lib/log.js +1 -1
- package/lib/sdk.js +96 -68
- package/lib/validate_config.js +7 -6
- package/package.json +30 -22
- package/server/columns.js +59 -12
- package/server/comms_js.js +3 -5
- package/server/data.js +145 -8
- package/server/index.js +52 -20
- package/server/index_html.js +17 -23
- package/server/json.js +27 -1
- package/server/views/default_template_index.html +1 -1
- package/server/views/index.html +9 -4
- package/site/embedded.js +1 -1
- package/site/images/data_type_date.svg +6 -0
- package/site/images/data_type_number.svg +5 -0
- package/site/images/data_type_region.svg +4 -0
- package/site/images/data_type_string.svg +5 -0
- package/site/images/montage.jpg +0 -0
- package/site/script.js +2 -1
- package/site/sdk.css +1 -1
- package/site/talk_to_server.js +1 -1
- package/test/lib/sdk.js +9 -0
- package/test/lib/validate_config.js +7 -5
- package/utils/state.js +2 -0
- package/test/mocha.opts +0 -2
package/server/data.js
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
8
8
|
|
|
9
|
+
var createInterpreter = require('@flourish/interpreter');
|
|
10
|
+
|
|
11
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
var createInterpreter__default = /*#__PURE__*/_interopDefaultLegacy(createInterpreter);
|
|
14
|
+
|
|
9
15
|
// Polyfills for IE11 and Edge
|
|
10
16
|
|
|
11
17
|
// Add findIndex method to Array
|
|
@@ -37,17 +43,47 @@ if (!Array.prototype.findIndex) {
|
|
|
37
43
|
});
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
function extractData(data_binding, data_by_id) {
|
|
46
|
+
function extractData(data_binding, data_by_id, column_types_by_id, template_data_binding) {
|
|
41
47
|
var columns = [];
|
|
42
48
|
var data_table_ids = [];
|
|
43
49
|
var num_rows = 0;
|
|
44
50
|
var dataset = [];
|
|
51
|
+
var interpreters_by_id = {};
|
|
45
52
|
dataset.column_names = {};
|
|
53
|
+
dataset.metadata = {};
|
|
54
|
+
|
|
55
|
+
function getInterpretationIds(data_table_id, column_index) {
|
|
56
|
+
if (!interpreters_by_id[data_table_id]) return {};
|
|
57
|
+
var by_column_index = interpreters_by_id[data_table_id];
|
|
58
|
+
if (!by_column_index[column_index]) return {};
|
|
59
|
+
return by_column_index[column_index];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getInterpreter(data_table_id, column_index) {
|
|
63
|
+
const { type_id } = getInterpretationIds(data_table_id, column_index);
|
|
64
|
+
if (type_id) return createInterpreter__default["default"].getInterpretation(type_id);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (var data_table_id in column_types_by_id) {
|
|
68
|
+
var lookup = {};
|
|
69
|
+
var column_types = column_types_by_id[data_table_id];
|
|
70
|
+
if (!column_types) continue;
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < column_types.length; i++) {
|
|
73
|
+
const type_id = column_types[i].type_id;
|
|
74
|
+
const of_id = column_types[i].output_format_id;
|
|
75
|
+
const output_format_id = (!of_id || of_id === "auto") ? type_id : of_id;
|
|
76
|
+
lookup[column_types[i].index] = { type_id, output_format_id };
|
|
77
|
+
}
|
|
78
|
+
interpreters_by_id[data_table_id] = lookup;
|
|
79
|
+
}
|
|
46
80
|
|
|
47
81
|
for (var key in data_binding) {
|
|
82
|
+
if (data_binding[key] === null) continue;
|
|
48
83
|
if (data_binding[key].columns === undefined && data_binding[key].column === undefined) continue;
|
|
49
84
|
|
|
50
85
|
var b = data_binding[key];
|
|
86
|
+
b.template_data_binding = template_data_binding[key];
|
|
51
87
|
b.key = key;
|
|
52
88
|
|
|
53
89
|
if (!(b.data_table_id in data_by_id)) {
|
|
@@ -68,11 +104,30 @@ function extractData(data_binding, data_by_id) {
|
|
|
68
104
|
var column_count = data_table[0].length;
|
|
69
105
|
b.columns = b.columns.filter(function(i) { return i < column_count; });
|
|
70
106
|
dataset.column_names[key] = b.columns.map(function(i) {
|
|
71
|
-
return
|
|
107
|
+
return data_table[0][i];
|
|
108
|
+
});
|
|
109
|
+
dataset.metadata[key] = b.columns.map(function(i) {
|
|
110
|
+
const { type_id, output_format_id } = getInterpretationIds(b.data_table_id, i);
|
|
111
|
+
if (type_id) {
|
|
112
|
+
return {
|
|
113
|
+
type: type_id.split("$")[0],
|
|
114
|
+
type_id,
|
|
115
|
+
output_format_id: output_format_id
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
72
119
|
});
|
|
73
120
|
}
|
|
74
121
|
else if ("column" in b && b.column != null) {
|
|
75
122
|
dataset.column_names[key] = data_table[0][b.column];
|
|
123
|
+
const { type_id, output_format_id } = getInterpretationIds(b.data_table_id, b.column);
|
|
124
|
+
if (type_id) {
|
|
125
|
+
dataset.metadata[key] = {
|
|
126
|
+
type: type_id.split("$")[0],
|
|
127
|
+
type_id,
|
|
128
|
+
output_format_id: output_format_id
|
|
129
|
+
};
|
|
130
|
+
}
|
|
76
131
|
}
|
|
77
132
|
else {
|
|
78
133
|
throw new Error("Data binding includes no column(s) specification: " + JSON.stringify(b));
|
|
@@ -80,11 +135,22 @@ function extractData(data_binding, data_by_id) {
|
|
|
80
135
|
|
|
81
136
|
if (data_table_ids.indexOf(b.data_table_id) == -1) {
|
|
82
137
|
data_table_ids.push(b.data_table_id);
|
|
83
|
-
num_rows = Math.max(num_rows,
|
|
138
|
+
num_rows = Math.max(num_rows, data_table.length - 1);
|
|
84
139
|
}
|
|
85
140
|
columns.push(b);
|
|
86
141
|
}
|
|
87
142
|
|
|
143
|
+
function parse(b, column_index, string_value) {
|
|
144
|
+
if (!b.template_data_binding.data_type) return string_value;
|
|
145
|
+
var interpreter = getInterpreter(b.data_table_id, column_index);
|
|
146
|
+
var result = interpreter ? interpreter.parse(string_value) : string_value;
|
|
147
|
+
|
|
148
|
+
// We require our marshalled data to be JSON-serialisable,
|
|
149
|
+
// therefore we convert NaNs to null here.
|
|
150
|
+
if (Number.isNaN(result)) result = null;
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
88
154
|
for (var i = 0; i < num_rows; i++) {
|
|
89
155
|
var o = {};
|
|
90
156
|
for (var j = 0; j < columns.length; j++) {
|
|
@@ -95,22 +161,29 @@ function extractData(data_binding, data_by_id) {
|
|
|
95
161
|
if ("columns" in b && b.columns != null) {
|
|
96
162
|
o[b.key] = b.columns
|
|
97
163
|
.filter(function(c) { return c < table[i+1].length; })
|
|
98
|
-
.map(function(c) { return table[i+1][c]; });
|
|
164
|
+
.map(function(c) { return parse(b, c, table[i+1][c]); });
|
|
99
165
|
}
|
|
100
166
|
else if ("column" in b && b.column != null) {
|
|
101
|
-
if (b.column >= table[i+1].length) o[b.key] = "";
|
|
102
|
-
else o[b.key] = table[i+1][b.column];
|
|
167
|
+
if (b.column >= table[i+1].length) o[b.key] = parse(b, b.column, "");
|
|
168
|
+
else o[b.key] = parse(b, b.column, table[i+1][b.column]);
|
|
103
169
|
}
|
|
104
170
|
}
|
|
105
171
|
dataset.push(o);
|
|
106
172
|
}
|
|
107
|
-
|
|
108
173
|
return dataset;
|
|
109
174
|
}
|
|
110
175
|
|
|
176
|
+
function getColumnTypesForData(data) {
|
|
177
|
+
return transposeNestedArray(data)
|
|
178
|
+
.map(function(d, i) {
|
|
179
|
+
const type_id = interpretColumn(d)[0].id;
|
|
180
|
+
return { type_id: type_id, index: i, output_format_id: type_id };
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
111
184
|
function trimTrailingEmptyRows(data) {
|
|
112
185
|
for (var i = data.length; i-- > 1;) {
|
|
113
|
-
if (!data[i] || !data[i].length || data[i].findIndex(function(col) { return col !== null && col !== ""; }) == -1) {
|
|
186
|
+
if (!data[i] || !data[i].length || (Array.isArray(data[i]) && data[i].findIndex(function(col) { return col !== null && col !== ""; }) == -1)) {
|
|
114
187
|
data.splice(i, 1);
|
|
115
188
|
}
|
|
116
189
|
else break;
|
|
@@ -118,6 +191,17 @@ function trimTrailingEmptyRows(data) {
|
|
|
118
191
|
return data;
|
|
119
192
|
}
|
|
120
193
|
|
|
194
|
+
function dropReturnCharacters(data) {
|
|
195
|
+
for (const row of data) {
|
|
196
|
+
for (let i = 0; i < row.length; i++) {
|
|
197
|
+
// Replace new-line character and surrounding whitespace with single space
|
|
198
|
+
// This fixes issue with pasting cells containing long strings from Excel into HoT
|
|
199
|
+
row[i] = row[i].replace(/(\r\n|\n|\r)/g, " ");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return data;
|
|
203
|
+
}
|
|
204
|
+
|
|
121
205
|
function trimWhitespace(data) {
|
|
122
206
|
data.forEach(function(row) {
|
|
123
207
|
for (var i=0; i < row.length; i++) {
|
|
@@ -127,6 +211,59 @@ function trimWhitespace(data) {
|
|
|
127
211
|
return data;
|
|
128
212
|
}
|
|
129
213
|
|
|
214
|
+
|
|
215
|
+
var ERROR_STRINGS = ["#DIV/0", "#N/A", "#NAME?", "#NULL!", "#NUM!", "#REF!", "#VALUE!", "#ERROR!"];
|
|
216
|
+
var interpreter = createInterpreter__default["default"]().nMax(Infinity).nFailingValues(8).failureFraction(0.1);
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
function stripCommonFixes(str) {
|
|
220
|
+
str = str || "";
|
|
221
|
+
return str.replace(/[€£$¥%º]/g, "");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
function transposeNestedArray(nested_array) {
|
|
226
|
+
var n_inner = nested_array.length;
|
|
227
|
+
var n_outer = nested_array[0].length;
|
|
228
|
+
var transposed_array = [];
|
|
229
|
+
|
|
230
|
+
for (var i = 0; i < n_outer; i++) {
|
|
231
|
+
var data = [];
|
|
232
|
+
for (var j = 0; j < n_inner; j++) {
|
|
233
|
+
data.push(nested_array[j][i]);
|
|
234
|
+
}
|
|
235
|
+
transposed_array.push(data);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return transposed_array;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
function getSlicedData(arr) {
|
|
243
|
+
const n = arr.length;
|
|
244
|
+
if (n > 100) return arr.slice(10, n - 10);
|
|
245
|
+
if (n > 50) return arr.slice(5, n - 5);
|
|
246
|
+
if (n > 30) return arr.slice(4, n - 4);
|
|
247
|
+
if (n > 20) return arr.slice(3, n - 3);
|
|
248
|
+
if (n > 10) return arr.slice(2, n - 2);
|
|
249
|
+
if (n > 1) return arr.slice(1, n);
|
|
250
|
+
return arr.slice(0, 1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
function interpretColumn(arr) {
|
|
255
|
+
var idata = arr.filter(function(d) {
|
|
256
|
+
return d && !ERROR_STRINGS.includes(d.trim());
|
|
257
|
+
})
|
|
258
|
+
.map(stripCommonFixes);
|
|
259
|
+
return interpreter(idata);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
exports.dropReturnCharacters = dropReturnCharacters;
|
|
130
263
|
exports.extractData = extractData;
|
|
264
|
+
exports.getColumnTypesForData = getColumnTypesForData;
|
|
265
|
+
exports.getSlicedData = getSlicedData;
|
|
266
|
+
exports.interpretColumn = interpretColumn;
|
|
267
|
+
exports.transposeNestedArray = transposeNestedArray;
|
|
131
268
|
exports.trimTrailingEmptyRows = trimTrailingEmptyRows;
|
|
132
269
|
exports.trimWhitespace = trimWhitespace;
|
package/server/index.js
CHANGED
|
@@ -9,7 +9,6 @@ const crypto = require("crypto"),
|
|
|
9
9
|
chokidar = require("chokidar"),
|
|
10
10
|
d3_dsv = require("d3-dsv"),
|
|
11
11
|
express = require("express"),
|
|
12
|
-
handlebars = require("handlebars"),
|
|
13
12
|
shell_quote = require("shell-quote"),
|
|
14
13
|
ws = require("ws"),
|
|
15
14
|
yaml = require("js-yaml"),
|
|
@@ -23,6 +22,9 @@ const crypto = require("crypto"),
|
|
|
23
22
|
log = require("../lib/log"),
|
|
24
23
|
sdk = require("../lib/sdk");
|
|
25
24
|
|
|
25
|
+
const { allowInsecurePrototypeAccess } = require("@handlebars/allow-prototype-access");
|
|
26
|
+
const handlebars = allowInsecurePrototypeAccess(require("handlebars"));
|
|
27
|
+
|
|
26
28
|
const TA = require("parse5/lib/tree-adapters/default.js");
|
|
27
29
|
|
|
28
30
|
// Generate a static prefix randomly
|
|
@@ -94,7 +96,7 @@ function loadJavaScript(template_dir) {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
function loadSettings(template_dir) {
|
|
97
|
-
return sdk.readAndValidateConfig(template_dir);
|
|
99
|
+
return sdk.readAndValidateConfig(template_dir).then(({config}) => config);
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
function listDataTables(template_dir) {
|
|
@@ -121,10 +123,15 @@ function getData(template_dir, data_tables) {
|
|
|
121
123
|
return Promise.all(data_tables.map((data_table) => getDataTable(template_dir, data_table)))
|
|
122
124
|
.then((data_array) => {
|
|
123
125
|
const data_by_name = {};
|
|
126
|
+
const column_types_by_name = {};
|
|
124
127
|
for (var i = 0; i < data_tables.length; i++) {
|
|
125
128
|
data_by_name[data_tables[i]] = data_array[i];
|
|
126
129
|
}
|
|
127
|
-
|
|
130
|
+
for (const data_table in data_by_name) {
|
|
131
|
+
const data = data_by_name[data_table];
|
|
132
|
+
column_types_by_name[data_table] = data_utils.getColumnTypesForData(data);
|
|
133
|
+
}
|
|
134
|
+
return { data: data_by_name, column_types_by_name };
|
|
128
135
|
});
|
|
129
136
|
}
|
|
130
137
|
|
|
@@ -139,7 +146,12 @@ function getDataTable(template_dir, data_table) {
|
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
function parseDataBindings(data_bindings, data_tables) {
|
|
142
|
-
if (!data_bindings)
|
|
149
|
+
if (!data_bindings) {
|
|
150
|
+
return {
|
|
151
|
+
data_bindings: {1: {}},
|
|
152
|
+
template_data_bindings: {1: {}}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
143
155
|
|
|
144
156
|
// Use the names as ids
|
|
145
157
|
const name_by_id = {};
|
|
@@ -147,14 +159,20 @@ function parseDataBindings(data_bindings, data_tables) {
|
|
|
147
159
|
|
|
148
160
|
// Collect parsed bindings by dataset
|
|
149
161
|
const data_bindings_by_dataset = {};
|
|
162
|
+
const template_data_bindings_by_dataset = {};
|
|
150
163
|
for (let binding of data_bindings) {
|
|
151
164
|
let dataset = binding.dataset;
|
|
152
165
|
if (!dataset) continue;
|
|
153
166
|
|
|
154
167
|
if (!data_bindings_by_dataset[dataset]) data_bindings_by_dataset[dataset] = {};
|
|
168
|
+
if (!template_data_bindings_by_dataset[dataset]) template_data_bindings_by_dataset[dataset] = {};
|
|
169
|
+
template_data_bindings_by_dataset[dataset][binding.key] = binding;
|
|
155
170
|
data_bindings_by_dataset[dataset][binding.key] = columns.parseDataBinding(binding, name_by_id);
|
|
156
171
|
}
|
|
157
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
data_bindings: { 1: data_bindings_by_dataset },
|
|
174
|
+
template_data_bindings: { 1: template_data_bindings_by_dataset }
|
|
175
|
+
};
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
function documentFragment(elements) {
|
|
@@ -176,23 +194,24 @@ function scriptElementExternal(url) {
|
|
|
176
194
|
}
|
|
177
195
|
|
|
178
196
|
|
|
179
|
-
function loadTemplate(template_dir, sdk_template, build_failed) {
|
|
197
|
+
function loadTemplate(template_dir, sdk_template, build_failed, options) {
|
|
180
198
|
return Promise.all([
|
|
181
199
|
listDataTables(template_dir),
|
|
182
200
|
loadSettings(template_dir),
|
|
183
201
|
])
|
|
184
202
|
.then(([data_tables, settings]) => {
|
|
185
|
-
const data_bindings = parseDataBindings(settings.data, data_tables);
|
|
203
|
+
const { data_bindings, template_data_bindings } = parseDataBindings(settings.data, data_tables);
|
|
186
204
|
return Promise.all([
|
|
187
205
|
settings, data_bindings, data_tables,
|
|
188
|
-
previewInitJs(template_dir, data_bindings["1"], data_tables),
|
|
206
|
+
previewInitJs(template_dir, template_data_bindings["1"], data_bindings["1"], data_tables),
|
|
189
207
|
loadTemplateText(template_dir),
|
|
190
|
-
loadJavaScript(template_dir)
|
|
208
|
+
loadJavaScript(template_dir),
|
|
209
|
+
getPublicUrlPrefix(options)
|
|
191
210
|
]);
|
|
192
211
|
})
|
|
193
212
|
.then(([
|
|
194
213
|
settings, data_bindings, data_tables,
|
|
195
|
-
preview_init_js, template_text, template_js
|
|
214
|
+
preview_init_js, template_text, template_js, public_url_prefix
|
|
196
215
|
]) => {
|
|
197
216
|
const page_params = {
|
|
198
217
|
// Always use ID of 1 for SDK
|
|
@@ -206,12 +225,13 @@ function loadTemplate(template_dir, sdk_template, build_failed) {
|
|
|
206
225
|
template_name: settings.name || "Untitled template",
|
|
207
226
|
template_version: settings.version,
|
|
208
227
|
template_author: settings.author || "",
|
|
209
|
-
build_failed: build_failed && build_failed.size > 0
|
|
228
|
+
build_failed: build_failed && build_failed.size > 0,
|
|
229
|
+
public_url_prefix
|
|
210
230
|
};
|
|
211
231
|
|
|
212
232
|
const script = documentFragment([
|
|
213
233
|
scriptElementInline("window.Flourish = " + json.safeStringify({
|
|
214
|
-
static_prefix, environment: "sdk"
|
|
234
|
+
static_prefix, environment: "sdk", is_read_only: false
|
|
215
235
|
}) + ";"),
|
|
216
236
|
scriptElementExternal("/template.js"),
|
|
217
237
|
scriptElementExternal("/comms.js"),
|
|
@@ -220,7 +240,7 @@ function loadTemplate(template_dir, sdk_template, build_failed) {
|
|
|
220
240
|
|
|
221
241
|
const preview_script = documentFragment([
|
|
222
242
|
scriptElementInline("window.Flourish = " + json.safeStringify({
|
|
223
|
-
static_prefix: preview_static_prefix, environment: "
|
|
243
|
+
static_prefix: preview_static_prefix, environment: "preview", is_read_only: true
|
|
224
244
|
}) + ";"),
|
|
225
245
|
scriptElementExternal("/template.js"),
|
|
226
246
|
scriptElementExternal("/comms.js"),
|
|
@@ -251,24 +271,31 @@ function loadTemplate(template_dir, sdk_template, build_failed) {
|
|
|
251
271
|
}));
|
|
252
272
|
}
|
|
253
273
|
|
|
254
|
-
function previewInitJs(template_dir, data_bindings, data_tables) {
|
|
255
|
-
return getData(template_dir, data_tables).then((data) => {
|
|
274
|
+
function previewInitJs(template_dir, template_data_bindings, data_bindings, data_tables) {
|
|
275
|
+
return getData(template_dir, data_tables).then(({data, column_types_by_name}) => {
|
|
256
276
|
const prepared_data = {};
|
|
257
277
|
for (let dataset in data_bindings) {
|
|
258
|
-
prepared_data[dataset] = data_utils.extractData(
|
|
278
|
+
prepared_data[dataset] = data_utils.extractData(
|
|
279
|
+
data_bindings[dataset], data, column_types_by_name,
|
|
280
|
+
template_data_bindings[dataset],
|
|
281
|
+
);
|
|
259
282
|
}
|
|
260
283
|
|
|
261
284
|
const column_names = {};
|
|
285
|
+
const metadata = {};
|
|
262
286
|
for (let dataset in prepared_data) {
|
|
263
287
|
column_names[dataset] = prepared_data[dataset].column_names;
|
|
288
|
+
metadata[dataset] = prepared_data[dataset].metadata;
|
|
264
289
|
}
|
|
265
290
|
|
|
266
291
|
return `
|
|
267
292
|
var _Flourish_data_column_names = ${json.safeStringify(column_names)},
|
|
268
|
-
|
|
293
|
+
_Flourish_data_metadata = ${json.safeStringify(metadata)},
|
|
294
|
+
_Flourish_data = ${json.javaScriptStringify(prepared_data)};
|
|
269
295
|
for (var _Flourish_dataset in _Flourish_data) {
|
|
270
296
|
window.template.data[_Flourish_dataset] = _Flourish_data[_Flourish_dataset];
|
|
271
297
|
window.template.data[_Flourish_dataset].column_names = _Flourish_data_column_names[_Flourish_dataset];
|
|
298
|
+
window.template.data[_Flourish_dataset].metadata = _Flourish_data_metadata[_Flourish_dataset];
|
|
272
299
|
}
|
|
273
300
|
window.template.draw();
|
|
274
301
|
`;
|
|
@@ -309,11 +336,16 @@ function splitPath(p) {
|
|
|
309
336
|
return p.split(path.sep).filter(c => c != "");
|
|
310
337
|
}
|
|
311
338
|
|
|
339
|
+
function getPublicUrlPrefix(options) {
|
|
340
|
+
return sdk.request(options, "config.json")
|
|
341
|
+
.then((config) => {
|
|
342
|
+
return config.PUBLIC_BUCKET_PREFIX;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
312
345
|
|
|
313
346
|
module.exports = function(template_dir, options) {
|
|
314
347
|
let app = express(),
|
|
315
348
|
reloadPreview,
|
|
316
|
-
|
|
317
349
|
template;
|
|
318
350
|
|
|
319
351
|
// Editor and settings/bindings
|
|
@@ -424,7 +456,7 @@ module.exports = function(template_dir, options) {
|
|
|
424
456
|
function _reloadTemplate() {
|
|
425
457
|
reload_timer = null;
|
|
426
458
|
log.info("Reloading...");
|
|
427
|
-
loadTemplate(template_dir, sdk_template, build_failed)
|
|
459
|
+
loadTemplate(template_dir, sdk_template, build_failed, options)
|
|
428
460
|
.then((template_) => {
|
|
429
461
|
template = template_;
|
|
430
462
|
log.info("Template reloaded. Trying to reload preview.");
|
|
@@ -517,7 +549,7 @@ module.exports = function(template_dir, options) {
|
|
|
517
549
|
loadSDKTemplate()
|
|
518
550
|
.then((sdk_template) => {
|
|
519
551
|
return Promise.all([
|
|
520
|
-
sdk_template, loadTemplate(template_dir, sdk_template)
|
|
552
|
+
sdk_template, loadTemplate(template_dir, sdk_template, undefined, options)
|
|
521
553
|
]);
|
|
522
554
|
})
|
|
523
555
|
.then(([sdk_template, template]) => {
|
package/server/index_html.js
CHANGED
|
@@ -8,7 +8,7 @@ const parse5 = require("parse5"),
|
|
|
8
8
|
const TA = require("parse5/lib/tree-adapters/default.js");
|
|
9
9
|
|
|
10
10
|
function findChild(node, nodeName, ok_if_not_found) {
|
|
11
|
-
for (
|
|
11
|
+
for (const child of TA.getChildNodes(node)) {
|
|
12
12
|
if (child.nodeName == nodeName) return child;
|
|
13
13
|
}
|
|
14
14
|
if (ok_if_not_found) return null;
|
|
@@ -27,26 +27,12 @@ function findBody(document) {
|
|
|
27
27
|
return findChild(findHtmlNode(document), "body");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function addAriaHiddenAttributeToBody(document) {
|
|
31
|
-
const body = findBody(document);
|
|
32
|
-
let found = false;
|
|
33
|
-
for (const attr of body.attrs) {
|
|
34
|
-
if (attr.name == "aria-hidden") {
|
|
35
|
-
attr.value = "true";
|
|
36
|
-
found = true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (!found) {
|
|
40
|
-
body.attrs.push({ name: "aria-hidden", value: "true" });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
30
|
function replaceTitle(document, title) {
|
|
45
31
|
const head = findHead(document);
|
|
46
32
|
let title_node = findChild(head, "title", true);
|
|
47
33
|
|
|
48
34
|
if (title_node) {
|
|
49
|
-
for (
|
|
35
|
+
for (const child of TA.getChildNodes(title_node)) {
|
|
50
36
|
TA.detachNode(child);
|
|
51
37
|
}
|
|
52
38
|
}
|
|
@@ -60,14 +46,24 @@ function replaceTitle(document, title) {
|
|
|
60
46
|
|
|
61
47
|
function appendFragmentToBody(document, fragment) {
|
|
62
48
|
const body = findBody(document);
|
|
63
|
-
for (
|
|
49
|
+
for (const child of TA.getChildNodes(fragment)) {
|
|
64
50
|
TA.appendChild(body, child);
|
|
65
51
|
}
|
|
66
52
|
}
|
|
67
53
|
|
|
54
|
+
function insertOembedLink(document, oembed_url) {
|
|
55
|
+
const head = findHead(document);
|
|
56
|
+
const link_node = TA.createElement("link", head.namespaceURI, [
|
|
57
|
+
{ name: "rel", value: "alternate" },
|
|
58
|
+
{ name: "type", value: "application/json+oembed" },
|
|
59
|
+
{ name: "href", value: oembed_url }
|
|
60
|
+
]);
|
|
61
|
+
TA.appendChild(head, link_node);
|
|
62
|
+
}
|
|
63
|
+
|
|
68
64
|
function insertCanonicalLink(document, canonical_url) {
|
|
69
65
|
const head = findHead(document);
|
|
70
|
-
|
|
66
|
+
const link_node = TA.createElement("link", head.namespaceURI, [
|
|
71
67
|
{ name: "rel", value: "canonical" },
|
|
72
68
|
{ name: "href", value: canonical_url }
|
|
73
69
|
]);
|
|
@@ -83,7 +79,7 @@ function rewriteLinks(document, static_prefix) {
|
|
|
83
79
|
// ... or relative self-links
|
|
84
80
|
if (url == "" || url == ".") return url;
|
|
85
81
|
|
|
86
|
-
return URL.resolve(static_prefix, url);
|
|
82
|
+
return URL.resolve(static_prefix, url); // eslint-disable-line node/no-deprecated-api
|
|
87
83
|
});
|
|
88
84
|
|
|
89
85
|
return rewriter.rewriteDocument(document);
|
|
@@ -93,14 +89,12 @@ function render(template_text, params) {
|
|
|
93
89
|
const document = parse5.parse(template_text),
|
|
94
90
|
script_fragment = params.parsed_script || parse5.parseFragment(params.script);
|
|
95
91
|
|
|
96
|
-
addAriaHiddenAttributeToBody(document);
|
|
97
92
|
replaceTitle(document, params.title);
|
|
98
93
|
if (params.canonical_url) insertCanonicalLink(document, params.canonical_url);
|
|
94
|
+
if (params.oembed_url) insertOembedLink(document, params.oembed_url);
|
|
99
95
|
appendFragmentToBody(document, script_fragment);
|
|
100
96
|
return rewriteLinks(document, params.static)
|
|
101
97
|
.then(parse5.serialize.bind(parse5));
|
|
102
98
|
}
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
render
|
|
106
|
-
};
|
|
100
|
+
exports.render = render;
|
package/server/json.js
CHANGED
|
@@ -20,8 +20,33 @@ function safeStringify(obj) {
|
|
|
20
20
|
return raw.replace(/[\u2028\u2029<]/g, escapeChar);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function javaScriptStringify(v) {
|
|
24
|
+
var type = typeof v;
|
|
25
|
+
if (v == null) {
|
|
26
|
+
// Catches both null and undefined
|
|
27
|
+
return "null";
|
|
28
|
+
}
|
|
29
|
+
else if (type === "number" || type === "boolean" || type === "bigint" || type === "string" || type === "symbol") {
|
|
30
|
+
return safeStringify(v);
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(v)) {
|
|
33
|
+
return "[" + v.map(javaScriptStringify).join(",") + "]";
|
|
34
|
+
}
|
|
35
|
+
else if (v instanceof Date) {
|
|
36
|
+
return "new Date(" + v.getTime() + ")";
|
|
37
|
+
}
|
|
38
|
+
else if (Object.prototype.toString.call(v) === "[object Object]") {
|
|
39
|
+
return "{" + Object.keys(v).map(function (k) {
|
|
40
|
+
return safeStringify(k) + ":" + javaScriptStringify(v[k]);
|
|
41
|
+
}) + "}";
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new Error("javaScriptStringify couldn't handle " + type + " object: " + v);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
23
48
|
function stringifyDataset(dataset) {
|
|
24
|
-
return "(function(array,
|
|
49
|
+
return "(function(array, column_names, metadata){ array.column_names = column_names; array.metadata = metadata; return array; })(" + javaScriptStringify(dataset) + ", " + safeStringify(dataset.column_names) + ", " + safeStringify(dataset.metadata) + ")";
|
|
25
50
|
}
|
|
26
51
|
|
|
27
52
|
function stringifyPreparedData(data) {
|
|
@@ -36,5 +61,6 @@ function stringifyPreparedData(data) {
|
|
|
36
61
|
return s;
|
|
37
62
|
}
|
|
38
63
|
|
|
64
|
+
exports.javaScriptStringify = javaScriptStringify;
|
|
39
65
|
exports.safeStringify = safeStringify;
|
|
40
66
|
exports.stringifyPreparedData = stringifyPreparedData;
|
package/server/views/index.html
CHANGED
|
@@ -50,8 +50,12 @@
|
|
|
50
50
|
<div id="visualisation" class="full-screen-ready">
|
|
51
51
|
<div id="visualisation-inner" class="editor-core">
|
|
52
52
|
<div class="preview-holder">
|
|
53
|
-
<img class="loading-spinner" src="/images/bosh.svg">
|
|
54
|
-
<
|
|
53
|
+
<img class="loading-spinner" src="/images/bosh.svg" data-testid="loading-indicator">
|
|
54
|
+
<div class="preview-overlay">
|
|
55
|
+
<p class="empty-label"></p>
|
|
56
|
+
<p class="empty-details"></p>
|
|
57
|
+
</div>
|
|
58
|
+
<iframe id="preview" sandbox="allow-same-origin allow-scripts allow-downloads" src="about:blank" data-testid="visualisation-iframe"></iframe>
|
|
55
59
|
<div id="resize-overlay"></div>
|
|
56
60
|
<div id="resize-handle-container">
|
|
57
61
|
<div id="resize-handle"></div>
|
|
@@ -68,6 +72,7 @@
|
|
|
68
72
|
</div>
|
|
69
73
|
|
|
70
74
|
<div class="template-settings"></div>
|
|
75
|
+
<div class="detailed-settings"></div>
|
|
71
76
|
</div>
|
|
72
77
|
</div>
|
|
73
78
|
</div>
|
|
@@ -87,8 +92,8 @@
|
|
|
87
92
|
</div>
|
|
88
93
|
|
|
89
94
|
<script>
|
|
90
|
-
Flourish.initSDK({{{ visualisation_js }}}, {{{ settings }}}, {{{ data_bindings }}});
|
|
91
|
-
Flourish.app.preview_pane.loadTemplate();
|
|
95
|
+
const sdk = Flourish.initSDK({{{ visualisation_js }}}, {{{ settings }}}, {{{ data_bindings }}}, "{{ public_url_prefix }}");
|
|
96
|
+
Flourish.app.preview_pane.loadTemplate(() => sdk.loadFromUrlHash());
|
|
92
97
|
Flourish.talkToServer();
|
|
93
98
|
</script>
|
|
94
99
|
</body>
|
package/site/embedded.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(){"use strict";var t,o,n,
|
|
1
|
+
!function(){"use strict";var e,i,t=!1;function n(e){if(t&&window.top!==window.self){var i=window;"srcdoc"===i.location.pathname&&(i=i.parent);var n,o=(n={},window._Flourish_template_id&&(n.template_id=window._Flourish_template_id),window.Flourish&&window.Flourish.app&&window.Flourish.app.loaded_template_id&&(n.template_id=window.Flourish.app.loaded_template_id),window._Flourish_visualisation_id&&(n.visualisation_id=window._Flourish_visualisation_id),window.Flourish&&window.Flourish.app&&window.Flourish.app.loaded_visualisation&&(n.visualisation_id=window.Flourish.app.loaded_visualisation.id),window.Flourish&&window.Flourish.app&&window.Flourish.app.story&&(n.story_id=window.Flourish.app.story.id,n.slide_count=window.Flourish.app.story.slides.length),window.Flourish&&window.Flourish.app&&window.Flourish.app.current_slide&&(n.slide_index=window.Flourish.app.current_slide.index+1),n),r={sender:"Flourish",method:"customerAnalytics"};for(var a in o)o.hasOwnProperty(a)&&(r[a]=o[a]);for(var a in e)e.hasOwnProperty(a)&&(r[a]=e[a]);i.parent.postMessage(JSON.stringify(r),"*")}}function o(e){if("function"!=typeof e)throw new Error("Analytics callback is not a function");window.Flourish._analytics_listeners.push(e)}function r(){t=!0;[{event_name:"click",action_name:"click",use_capture:!0},{event_name:"keydown",action_name:"key_down",use_capture:!0},{event_name:"mouseenter",action_name:"mouse_enter",use_capture:!1},{event_name:"mouseleave",action_name:"mouse_leave",use_capture:!1}].forEach((function(e){document.body.addEventListener(e.event_name,(function(){n({action:e.action_name})}),e.use_capture)}))}function a(){if(null==e){var i=function(){var e=window.location;"about:srcdoc"==e.href&&(e=window.parent.location);var i={};return function(e,t,n){for(;n=t.exec(e);)i[decodeURIComponent(n[1])]=decodeURIComponent(n[2])}(e.search.substring(1).replace(/\+/g,"%20"),/([^&=]+)=?([^&]*)/g),i}();e="referrer"in i?/^https:\/\/medium.com\//.test(i.referrer):!("auto"in i)}return e}function s(e){var i=e||window.innerWidth;return i>999?650:i>599?575:400}function l(e,t){if(window.top!==window.self){var n=window;if("srcdoc"==n.location.pathname&&(n=n.parent),i)return e=parseInt(e,10),void n.parent.postMessage({sentinel:"amp",type:"embed-size",height:e},"*");var o={sender:"Flourish",context:"iframe.resize",method:"resize",height:e,src:n.location.toString()};if(t)for(var r in t)o[r]=t[r];n.parent.postMessage(JSON.stringify(o),"*")}}function d(){return(-1!==navigator.userAgent.indexOf("Safari")||-1!==navigator.userAgent.indexOf("iPhone"))&&-1==navigator.userAgent.indexOf("Chrome")}function u(e){window.addEventListener("message",(function(i){if(null!=i.source&&(i.origin===document.location.origin||i.origin.match(/\/\/localhost:\d+$|\/\/flourish-api\.com$|\.flourish\.(?:local(:\d+)?|net|rocks|studio)$|\.uri\.sh$/))){var t;try{t=JSON.parse(i.data)}catch(e){return void console.warn("Unexpected non-JSON message: "+JSON.stringify(i.data))}if("Flourish"===t.sender){for(var n=document.querySelectorAll("iframe"),o=0;o<n.length;o++)if(n[o].contentWindow==i.source||n[o].contentWindow==i.source.parent)return void e(t,n[o]);console.warn("could not find frame",t)}}})),d()&&(window.addEventListener("resize",h),h())}function h(){for(var e=document.querySelectorAll(".flourish-embed"),i=0;i<e.length;i++){var t=e[i];if(!t.getAttribute("data-width")){var n=t.querySelector("iframe");if(n){var o=window.getComputedStyle(t),r=t.offsetWidth-parseFloat(o.paddingLeft)-parseFloat(o.paddingRight);n.style.width=r+"px"}}}}function c(e,i,t,n,o){var r=document.createElement("iframe");if(r.setAttribute("scrolling","no"),r.setAttribute("frameborder","0"),r.setAttribute("title","Interactive or visual content"),r.setAttribute("sandbox","allow-same-origin allow-forms allow-scripts allow-downloads allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"),i.appendChild(r),r.offsetParent||"fixed"===getComputedStyle(r).position)f(e,i,r,t,n,o);else{var a={embed_url:e,container:i,iframe:r,width:t,height:n,play_on_load:o};if(window._flourish_poll_items?window._flourish_poll_items.push(a):window._flourish_poll_items=[a],window._flourish_poll_items.length>1)return r;var s=setInterval((function(){window._flourish_poll_items=window._flourish_poll_items.filter((function(e){return!e.iframe.offsetParent||(f(e.embed_url,e.container,e.iframe,e.width,e.height,e.play_on_load),!1)})),window._flourish_poll_items.length||clearInterval(s)}),500)}return r}function f(e,i,t,n,o,r){var a;return n&&"number"==typeof n?(a=n,n+="px"):n&&n.match(/^[ \t\r\n\f]*([+-]?\d+|\d*\.\d+(?:[eE][+-]?\d+)?)(?:\\?[Pp]|\\0{0,4}[57]0(?:\r\n|[ \t\r\n\f])?)(?:\\?[Xx]|\\0{0,4}[57]8(?:\r\n|[ \t\r\n\f])?)[ \t\r\n\f]*$/)&&(a=parseFloat(n)),o&&"number"==typeof o&&(o+="px"),n?t.style.width=n:d()?t.style.width=i.offsetWidth+"px":t.style.width="100%",!!o||(e.match(/\?/)?e+="&auto=1":e+="?auto=1",o=s(a||t.offsetWidth)+"px"),o&&("%"===o.charAt(o.length-1)&&(o=parseFloat(o)/100*i.parentNode.offsetHeight+"px"),t.style.height=o),t.setAttribute("src",e+(r?"#play-on-load":"")),t}function w(e){return!Array.isArray(e)&&"object"==typeof e&&null!=e}function p(e,i){for(var t in i)w(e[t])&&w(i[t])?p(e[t],i[t]):e[t]=i[t];return e}!function(){var e,t=window.top===window.self,h=t?null:(i="#amp=1"==window.location.hash,{createEmbedIframe:c,isFixedHeight:a,getHeightForBreakpoint:s,startEventListeners:u,notifyParentWindow:l,isSafari:d,initCustomerAnalytics:r,addAnalyticsListener:o,sendCustomerAnalyticsMessage:n}),f=!0;function w(){var i;Flourish.fixed_height||(null!=e?i=e:f&&(i=h.getHeightForBreakpoint()),i!==window.innerHeight&&h.notifyParentWindow(i))}function m(){w(),window.addEventListener("resize",w)}Flourish.warn=function(e){if("string"==typeof e&&(e={message:e}),t||"editor"!==Flourish.environment)console.warn(e.message);else{var i={sender:"Flourish",method:"warn",message:e.message,explanation:e.explanation};window.parent.postMessage(JSON.stringify(i),"*")}},Flourish.uploadImage=function(e){if(t||"story_editor"!==Flourish.environment)throw"Invalid upload request";var i={sender:"Flourish",method:"request-upload",name:e.name,accept:e.accept};window.parent.postMessage(JSON.stringify(i),"*")},Flourish.setSetting=function(e,i){if("editor"===Flourish.environment||"sdk"===Flourish.environment){var t={sender:"Flourish",method:"setSetting",name:e,value:i};window.parent.postMessage(JSON.stringify(t),"*")}else if("story_editor"===Flourish.environment){var n={};n[e]=i,p(window.template.state,function(e){var i={};for(var t in e){for(var n=i,o=t.indexOf("."),r=0;o>=0;o=t.indexOf(".",r=o+1)){var a=t.substring(r,o);a in n||(n[a]={}),n=n[a]}n[t.substring(r)]=e[t]}return i}(n))}},Flourish.setHeight=function(i){Flourish.fixed_height||(e=i,f=null==i,w())},Flourish.checkHeight=function(){if(!t){var e=Flourish.__container_height;null!=e?(Flourish.fixed_height=!0,h.notifyParentWindow(e)):h.isFixedHeight()?Flourish.fixed_height=!0:(Flourish.fixed_height=!1,w())}},Flourish.fixed_height=t||h.isFixedHeight(),Flourish.enableCustomerAnalytics=function(){h&&h.initCustomerAnalytics()},"loading"===document.readyState?document.addEventListener("DOMContentLoaded",m):m()}()}();
|
|
2
2
|
//# sourceMappingURL=embedded.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect x="0.5" y="1.5" width="9" height="8" rx="1.5" stroke="white"/>
|
|
3
|
+
<line x1="1" y1="4.5" x2="9" y2="4.5" stroke="white"/>
|
|
4
|
+
<line x1="2.5" x2="2.13636" y2="2" stroke="white" stroke-linejoin="round"/>
|
|
5
|
+
<line x1="7.5" x2="7.13636" y2="2" stroke="white" stroke-linejoin="round"/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="12" height="5" viewBox="0 0 12 5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M0 4.90895V4.00607H1.04704V1.22155H0.144158V0.531108C0.407183 0.480526 0.629742 0.419828 0.811836 0.349014C0.99393 0.278199 1.17097 0.19221 1.34294 0.0910468H2.16237V4.00607H3.06525V4.90895H0Z" fill="white"/>
|
|
3
|
+
<path d="M4.10122 4.90895V4.27162C4.40977 3.98331 4.6905 3.71522 4.94341 3.46737C5.20137 3.21447 5.4214 2.97926 5.6035 2.76176C5.79065 2.5392 5.93481 2.33434 6.03597 2.14719C6.14219 1.95498 6.1953 1.77289 6.1953 1.60091C6.1953 1.36318 6.13461 1.18361 6.01321 1.06222C5.89181 0.935761 5.72489 0.872534 5.51245 0.872534C5.33541 0.872534 5.17861 0.923116 5.04204 1.02428C4.90547 1.12038 4.77649 1.23419 4.65509 1.36571L4.04811 0.766313C4.28584 0.513404 4.52864 0.323723 4.77649 0.197268C5.02434 0.0657561 5.32024 0 5.6642 0C5.90193 0 6.1169 0.0379362 6.30911 0.113809C6.50638 0.184623 6.67583 0.288316 6.81746 0.424886C6.95909 0.556399 7.06784 0.715731 7.14371 0.902883C7.21958 1.09004 7.25752 1.29995 7.25752 1.53263C7.25752 1.73495 7.21452 1.94234 7.12854 2.15478C7.04255 2.36216 6.92621 2.57208 6.77952 2.78452C6.63789 2.99191 6.4735 3.20182 6.28635 3.41426C6.10426 3.62165 5.91457 3.8265 5.71731 4.02883C5.8387 4.01366 5.97274 4.00101 6.11943 3.9909C6.27118 3.97572 6.40522 3.96813 6.52155 3.96813H7.49272V4.90895H4.10122Z" fill="white"/>
|
|
4
|
+
<path d="M9.98462 5C9.59008 5 9.25624 4.9393 8.9831 4.81791C8.70996 4.69145 8.48487 4.52453 8.30783 4.31715L8.82377 3.61153C8.97045 3.75316 9.12978 3.8695 9.30176 3.96055C9.4788 4.05159 9.67101 4.09712 9.87839 4.09712C10.1161 4.09712 10.3058 4.04906 10.4474 3.95296C10.5891 3.8518 10.6599 3.71017 10.6599 3.52807C10.6599 3.42185 10.6396 3.32575 10.5992 3.23976C10.5638 3.15377 10.498 3.08295 10.4019 3.02731C10.3058 2.96662 10.1743 2.92109 10.0074 2.89074C9.84046 2.85534 9.62549 2.83763 9.36246 2.83763V2.04856C9.57996 2.04856 9.75953 2.03338 9.90116 2.00303C10.0478 1.97269 10.1642 1.92969 10.2502 1.87405C10.3412 1.81335 10.4044 1.74507 10.4399 1.6692C10.4803 1.58826 10.5005 1.49975 10.5005 1.40364C10.5005 1.23672 10.45 1.10774 10.3488 1.01669C10.2476 0.920587 10.101 0.872534 9.90874 0.872534C9.73677 0.872534 9.57996 0.91047 9.43833 0.986343C9.30176 1.06222 9.1576 1.16591 9.00586 1.29742L8.4444 0.614567C8.66696 0.422357 8.89964 0.273141 9.14243 0.16692C9.39028 0.0556399 9.66342 0 9.96185 0C10.2097 0 10.4348 0.0303489 10.6371 0.0910468C10.8445 0.146687 11.019 0.232676 11.1606 0.349014C11.3073 0.460293 11.4211 0.596864 11.5021 0.758725C11.583 0.920586 11.6235 1.10774 11.6235 1.32018C11.6235 1.57309 11.5526 1.78806 11.411 1.9651C11.2744 2.13708 11.0772 2.27871 10.8192 2.38998V2.42033C11.0974 2.50126 11.325 2.64036 11.5021 2.83763C11.6842 3.02984 11.7752 3.28275 11.7752 3.59636C11.7752 3.81892 11.7272 4.01872 11.6311 4.19575C11.5349 4.36773 11.406 4.51442 11.2441 4.63581C11.0822 4.75215 10.8926 4.8432 10.6751 4.90895C10.4576 4.96965 10.2274 5 9.98462 5Z" fill="white"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="9" height="10" viewBox="0 0 9 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M8.94856 5.94937C8.92747 5.82279 8.85363 5.71731 8.73759 5.65402L6.70173 4.64136C6.42747 5.20043 6.06882 5.89663 5.58359 6.74051C5.35152 7.14136 4.94013 7.38397 4.47599 7.38397C4.01186 7.38397 3.58992 7.14136 3.35785 6.74051C2.91481 5.98102 2.57726 5.34811 2.32409 4.82068L1.18485 5.01056C1.05827 5.03165 0.952785 5.11604 0.900042 5.22153L0.0350632 7.173C-0.049325 7.35233 0.0245146 7.57385 0.193291 7.65823L4.38105 9.95781C4.4338 9.98946 4.49709 10 4.56038 10C4.58148 10 4.61312 10 4.63422 9.98946L8.21017 9.25106C8.3262 9.22997 8.42114 9.15613 8.47388 9.05064C8.52662 8.94516 8.52662 8.82912 8.48443 8.72364L7.78823 7.16245L8.83253 6.26583C8.92747 6.20254 8.96966 6.07596 8.94856 5.94937Z" fill="white"/>
|
|
3
|
+
<path d="M4.92955 6.37131C5.58356 5.23207 6.7228 3.13291 6.7228 2.24684C6.7228 1.00211 5.71014 0 4.47596 0C3.24178 0 2.22913 1.01266 2.22913 2.24684C2.22913 3.13291 3.36837 5.23207 4.02237 6.37131C4.2228 6.71941 4.72913 6.71941 4.92955 6.37131ZM3.43166 2.23629C3.43166 1.65612 3.90634 1.19198 4.47596 1.19198C5.04558 1.19198 5.52027 1.66667 5.52027 2.23629C5.52027 2.81646 5.04558 3.28059 4.47596 3.28059C3.90634 3.28059 3.43166 2.81646 3.43166 2.23629Z" fill="white"/>
|
|
4
|
+
</svg>
|