djangocms-render-context 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
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.
- djangocms_render_context/__init__.py +1 -1
- djangocms_render_context/cms_plugins.py +9 -0
- djangocms_render_context/forms.py +68 -3
- djangocms_render_context/loaders.py +21 -1
- djangocms_render_context/locale/cs/LC_MESSAGES/django.mo +0 -0
- djangocms_render_context/locale/cs/LC_MESSAGES/django.po +41 -7
- djangocms_render_context/locale/cs/LC_MESSAGES/djangojs.mo +0 -0
- djangocms_render_context/locale/cs/LC_MESSAGES/djangojs.po +130 -0
- djangocms_render_context/migrations/0001_initial.py +0 -2
- djangocms_render_context/migrations/0002_rendercontext_mimetype.py +33 -0
- djangocms_render_context/migrations/0003_rendercontext_config_and_path.py +46 -0
- djangocms_render_context/models.py +49 -8
- djangocms_render_context/static/djangocms_render_context/css-dist/icon-family-Material-Icons.css +20 -0
- djangocms_render_context/static/djangocms_render_context/css-dist/jspreadsheet.min.css +8 -0
- djangocms_render_context/static/djangocms_render_context/css-dist/jsuites.min.css +8 -0
- djangocms_render_context/static/djangocms_render_context/js/spreadsheet.js +711 -0
- djangocms_render_context/static/djangocms_render_context/js-dist/csv.min.js +1 -0
- djangocms_render_context/static/djangocms_render_context/js-dist/index.min.js +8 -0
- djangocms_render_context/static/djangocms_render_context/js-dist/index.min.js.map +1 -0
- djangocms_render_context/static/djangocms_render_context/js-dist/js-yaml.min.js +2 -0
- djangocms_render_context/static/djangocms_render_context/js-dist/jsuites.min.js +8 -0
- djangocms_render_context/static/djangocms_render_context/js-dist/jsuites.min.js.map +1 -0
- djangocms_render_context/utils.py +16 -0
- {djangocms_render_context-1.0.0.dist-info → djangocms_render_context-1.2.0.dist-info}/METADATA +17 -1
- djangocms_render_context-1.2.0.dist-info/RECORD +31 -0
- {djangocms_render_context-1.0.0.dist-info → djangocms_render_context-1.2.0.dist-info}/WHEEL +1 -1
- djangocms_render_context-1.0.0.dist-info/RECORD +0 -17
- {djangocms_render_context-1.0.0.dist-info → djangocms_render_context-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
// https://bossanova.uk/jspreadsheet/docs/getting-started
|
|
2
|
+
// https://github.com/jspreadsheet/editors
|
|
3
|
+
// https://jsuites.net/docs/javascript-html-editor
|
|
4
|
+
// https://jsuites.net/docs/v4/javascript-html-editor
|
|
5
|
+
/*
|
|
6
|
+
Available Editors:
|
|
7
|
+
text
|
|
8
|
+
numeric
|
|
9
|
+
hidden
|
|
10
|
+
dropdown
|
|
11
|
+
checkbox
|
|
12
|
+
radio
|
|
13
|
+
calendar
|
|
14
|
+
image
|
|
15
|
+
color
|
|
16
|
+
html
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
(function() {
|
|
20
|
+
|
|
21
|
+
const spreadsheetId = "id-spreadsheet"
|
|
22
|
+
|
|
23
|
+
function parseData(src, mimetype) {
|
|
24
|
+
let data = null
|
|
25
|
+
if (src.value) {
|
|
26
|
+
if (mimetype === "application/json") {
|
|
27
|
+
data = JSON.parse(src.value)
|
|
28
|
+
} else if (mimetype === "text/csv") {
|
|
29
|
+
data = CSV.parse(src.value)
|
|
30
|
+
} else if (mimetype === "application/yaml") {
|
|
31
|
+
data = jsyaml.load(src.value)
|
|
32
|
+
} else if (mimetype === "application/xml") {
|
|
33
|
+
data = parseXml(src.value)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return data
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseXml(src) {
|
|
40
|
+
const data = {}
|
|
41
|
+
const parser = new DOMParser()
|
|
42
|
+
const doc = parser.parseFromString(src, "text/xml")
|
|
43
|
+
const root = doc.querySelector("*")
|
|
44
|
+
data[root.tagName] = {}
|
|
45
|
+
if (!root.children.length) {
|
|
46
|
+
return data
|
|
47
|
+
}
|
|
48
|
+
const table = []
|
|
49
|
+
for(const row of root.children) {
|
|
50
|
+
const line = {}
|
|
51
|
+
for(const column of row.children) {
|
|
52
|
+
line[column.tagName] = column.innerHTML
|
|
53
|
+
}
|
|
54
|
+
table.push(line)
|
|
55
|
+
}
|
|
56
|
+
data[root.tagName][root.children[0].tagName] = table
|
|
57
|
+
return data
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function objectToXml(obj, params, level, name) {
|
|
61
|
+
let xml = ""
|
|
62
|
+
if (obj instanceof Object) {
|
|
63
|
+
const rootMissing = name === undefined && level === 0 && params.options.tableNameMissing
|
|
64
|
+
if (rootMissing) {
|
|
65
|
+
xml += `<${params.options.table}>`
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(obj)) {
|
|
68
|
+
const subname = name === undefined ? (level ? params.options.table : params.options.row) : name
|
|
69
|
+
const columnMissing = level > 0 && params.options.columnName
|
|
70
|
+
if (columnMissing) {
|
|
71
|
+
xml += `<${params.options.columnName}>`
|
|
72
|
+
}
|
|
73
|
+
for(let key=0; key < obj.length; key++) {
|
|
74
|
+
xml += objectToXml(obj[key], params, level+1, subname)
|
|
75
|
+
}
|
|
76
|
+
if (columnMissing) {
|
|
77
|
+
xml += `</${params.options.columnName}>`
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
if (name !== undefined) {
|
|
81
|
+
xml += `<${name}>`
|
|
82
|
+
}
|
|
83
|
+
for (const key in obj) {
|
|
84
|
+
xml += objectToXml(obj[key], params, level+1, key)
|
|
85
|
+
}
|
|
86
|
+
if (name !== undefined) {
|
|
87
|
+
xml += `</${name}>`
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (rootMissing) {
|
|
91
|
+
xml += `</${params.options.table}>`
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
xml += name !== undefined ? `<${name}>${obj}</${name}>` : obj
|
|
95
|
+
}
|
|
96
|
+
return xml
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function formatXml(xml) {
|
|
100
|
+
const PADDING = " "
|
|
101
|
+
const reg = /(>)(<)(\/*)/g
|
|
102
|
+
let pad = 0
|
|
103
|
+
xml = xml.replace(reg, '$1\r\n$2$3')
|
|
104
|
+
return xml.split('\r\n').map((node) => {
|
|
105
|
+
let indent = 0
|
|
106
|
+
if (node.match(/.+<\/\w[^>]*>$/)) {
|
|
107
|
+
indent = 0
|
|
108
|
+
} else if (node.match(/^<\/\w/)) {
|
|
109
|
+
if (pad !== 0) pad -= 1
|
|
110
|
+
} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
|
|
111
|
+
indent = 1
|
|
112
|
+
}
|
|
113
|
+
const padding = PADDING.repeat(pad)
|
|
114
|
+
pad += indent
|
|
115
|
+
return padding + node
|
|
116
|
+
}).join('\r\n')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function dumpXml(data, params) {
|
|
120
|
+
if (Array.isArray(data)) {
|
|
121
|
+
params.options.tableNameMissing = true
|
|
122
|
+
if (data.length && Array.isArray(data[0])) {
|
|
123
|
+
params.options.columnName = "item"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (isObj(data)) {
|
|
127
|
+
for (const key in data) {
|
|
128
|
+
if (Array.isArray(data[key])) {
|
|
129
|
+
params.options.tableNameMissing = true
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return formatXml(objectToXml(data, params, 0))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
function setDataIntoContext(data, params) {
|
|
139
|
+
const mimetype = document.querySelector("select[name=mimetype]").value
|
|
140
|
+
if (mimetype === "text/csv" && isObj(data)) {
|
|
141
|
+
throw new Error(gettext("This data cannot be serialized in the selected format. Please select a different format."))
|
|
142
|
+
}
|
|
143
|
+
let value
|
|
144
|
+
if (mimetype === "application/json") {
|
|
145
|
+
value = JSON.stringify(data, null, 2)
|
|
146
|
+
} else if (mimetype === "text/csv") {
|
|
147
|
+
value = CSV.serialize(data)
|
|
148
|
+
} else if (mimetype === "application/yaml") {
|
|
149
|
+
value = jsyaml.dump(data)
|
|
150
|
+
} else if (mimetype === "application/xml") {
|
|
151
|
+
value = dumpXml(data, params)
|
|
152
|
+
}
|
|
153
|
+
document.querySelector("textarea[name=context]").value = value
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function stripCells(row) {
|
|
157
|
+
const cells = []
|
|
158
|
+
let isContent = false
|
|
159
|
+
for (let i = row.length - 1; i >= 0; i--) {
|
|
160
|
+
if (isContent || row[i]) {
|
|
161
|
+
isContent = true
|
|
162
|
+
cells.push(row[i])
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return cells.reverse()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function removeEmptyRows(data) {
|
|
169
|
+
const table = []
|
|
170
|
+
let isContent = false
|
|
171
|
+
for (let i = data.length - 1; i >= 0; i--) {
|
|
172
|
+
const cells = stripCells(data[i])
|
|
173
|
+
if (isContent || cells.length) {
|
|
174
|
+
isContent = true
|
|
175
|
+
table.push(cells.length ? cells : data[i])
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return table.reverse()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function createSpreadsheetNode() {
|
|
182
|
+
const context = document.querySelector("textarea[name=context]")
|
|
183
|
+
const node = document.createElement("div")
|
|
184
|
+
node.id = spreadsheetId
|
|
185
|
+
context.after(node)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function setSpreadsheetTranslations() {
|
|
189
|
+
window.jspreadsheet.setDictionary({
|
|
190
|
+
'Are you sure?': gettext('Are you sure?'),
|
|
191
|
+
'Rename this cell': gettext('Rename this cell'),
|
|
192
|
+
'Cut': gettext('Cut'),
|
|
193
|
+
'Copy': gettext('Copy'),
|
|
194
|
+
'Paste': gettext('Paste'),
|
|
195
|
+
'Insert a new column before': gettext('Insert a new column before'),
|
|
196
|
+
'Insert a new column after': gettext('Insert a new column after'),
|
|
197
|
+
'Insert a new row before': gettext('Insert a new row before'),
|
|
198
|
+
'Insert a new row after': gettext('Insert a new row after'),
|
|
199
|
+
'Delete selected columns': gettext('Delete selected columns'),
|
|
200
|
+
'Delete selected rows': gettext('Delete selected rows'),
|
|
201
|
+
'Rename this column': gettext('Rename this column'),
|
|
202
|
+
'Add comments': gettext('Add comments'),
|
|
203
|
+
'Edit comments': gettext('Edit comments'),
|
|
204
|
+
'Clear comments': gettext('Clear comments'),
|
|
205
|
+
'Order ascending': gettext('Order ascending'),
|
|
206
|
+
'Order descending': gettext('Order descending'),
|
|
207
|
+
'Save as': gettext('Save as'),
|
|
208
|
+
'About': gettext('About'),
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function displayError(message, title) {
|
|
213
|
+
if (!title) {
|
|
214
|
+
title = gettext("Error message")
|
|
215
|
+
}
|
|
216
|
+
jSuites.notification({
|
|
217
|
+
error: 1,
|
|
218
|
+
timeout: 60000,
|
|
219
|
+
// autoHide: false,
|
|
220
|
+
name: title,
|
|
221
|
+
message: message
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function isObj(value) {
|
|
226
|
+
// Check if value is object.
|
|
227
|
+
return value instanceof Object && !Array.isArray(value)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function getPathsToArrays(obj, parent = "") {
|
|
231
|
+
let result = []
|
|
232
|
+
if (isObj(obj)) {
|
|
233
|
+
for (let key in obj) {
|
|
234
|
+
const path = parent ? `${parent}.${key}` : key
|
|
235
|
+
if (Array.isArray(obj[key])) {
|
|
236
|
+
result.push(path)
|
|
237
|
+
} else if (isObj(obj[key])) {
|
|
238
|
+
result = result.concat(getPathsToArrays(obj[key], path))
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return result
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function getPayload(obj, path) {
|
|
246
|
+
// Get objects data for the path.
|
|
247
|
+
path.split(".").forEach((key) => {
|
|
248
|
+
obj = obj[key]
|
|
249
|
+
})
|
|
250
|
+
return obj
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function setPayload(obj, path, payload) {
|
|
254
|
+
// Set data into the object path.
|
|
255
|
+
const names = path.split(".")
|
|
256
|
+
const key = names.pop()
|
|
257
|
+
for(let i=0; i < names.length; i++) {
|
|
258
|
+
obj = obj[names[i]]
|
|
259
|
+
}
|
|
260
|
+
obj[key] = payload
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function resolveColumnType(value) {
|
|
264
|
+
let type = "text"
|
|
265
|
+
if (Number.isInteger(value)) {
|
|
266
|
+
type = "numeric"
|
|
267
|
+
} else if (typeof value == "boolean") {
|
|
268
|
+
type = "checkbox"
|
|
269
|
+
} else if (typeof value === 'string') {
|
|
270
|
+
if (value.match(/#[A-Fa-f0-9]{6}/)) {
|
|
271
|
+
type = "color"
|
|
272
|
+
} else if (value.match(/\d{4}-\d{2}-\d{2}/)) {
|
|
273
|
+
type = "calendar"
|
|
274
|
+
} else if (value.match(/<.+>/)) {
|
|
275
|
+
type = "html"
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return type
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function getContextData(mimetype) {
|
|
282
|
+
return parseData(document.querySelector("textarea[name=context]"), mimetype)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function getTableName(path) {
|
|
286
|
+
const names = path.split(".").reverse()
|
|
287
|
+
return names.length > 1 ? names[1] : "table"
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function getTableRowName(path) {
|
|
291
|
+
const names = path.split(".").reverse()
|
|
292
|
+
return names.length > 0 ? names[0] : "row"
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
function numberToColumn(num) {
|
|
297
|
+
let result = ""
|
|
298
|
+
while (num > 0) {
|
|
299
|
+
const remainder = (num - 1) % 26
|
|
300
|
+
result = String.fromCharCode(65 + remainder) + result
|
|
301
|
+
num = Math.floor((num - remainder) / 26)
|
|
302
|
+
}
|
|
303
|
+
return result
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function convertDataToTableAndOptions(data) {
|
|
307
|
+
const tableData = [], tableOptions = {}
|
|
308
|
+
if (Array.isArray(data)) {
|
|
309
|
+
for(let r = 0; r < data.length; r++) {
|
|
310
|
+
const row = data[r]
|
|
311
|
+
if (Array.isArray(row)) {
|
|
312
|
+
tableData.push(row)
|
|
313
|
+
if (tableOptions.types === undefined) {
|
|
314
|
+
tableOptions.types = []
|
|
315
|
+
for(let c = 0; c < row.length; c++) {
|
|
316
|
+
tableOptions.types.push(resolveColumnType(row[c]))
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} else if (isObj(row)) {
|
|
320
|
+
const columns = []
|
|
321
|
+
for (const [key, value] of Object.entries(row)) {
|
|
322
|
+
columns.push(value)
|
|
323
|
+
}
|
|
324
|
+
tableData.push(columns)
|
|
325
|
+
if (tableOptions.types === undefined) {
|
|
326
|
+
tableOptions.titles = []
|
|
327
|
+
tableOptions.types = []
|
|
328
|
+
for (const [key, value] of Object.entries(row)) {
|
|
329
|
+
tableOptions.titles.push(key)
|
|
330
|
+
tableOptions.types.push(resolveColumnType(value))
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
tableOptions.types = [resolveColumnType(row)]
|
|
335
|
+
tableData.push([row])
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!tableData.length) {
|
|
339
|
+
tableOptions.types = ["text"]
|
|
340
|
+
tableData.push([""])
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
tableData.push([data])
|
|
344
|
+
tableOptions.types = [resolveColumnType(data)]
|
|
345
|
+
}
|
|
346
|
+
return [tableData, tableOptions]
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
function addLink() {
|
|
351
|
+
const href = prompt(gettext("Enter a URL:"), "https://")
|
|
352
|
+
if (href) {
|
|
353
|
+
const sel = getSelection()
|
|
354
|
+
const label = sel.toString()
|
|
355
|
+
const link = document.createElement("a")
|
|
356
|
+
link.href = href
|
|
357
|
+
link.appendChild(document.createTextNode(label ? label : href))
|
|
358
|
+
const range = sel.getRangeAt(0)
|
|
359
|
+
range.deleteContents()
|
|
360
|
+
range.insertNode(link)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function onCreateEditor(sheet, td, x, y, empty, options) {
|
|
365
|
+
if (options.type === "html") {
|
|
366
|
+
setTimeout(() => {
|
|
367
|
+
const richtext = document.querySelector(".jss_richtext")
|
|
368
|
+
if (richtext) {
|
|
369
|
+
const item = richtext.querySelector(".jeditor-toolbar > div > div:nth-child(6)")
|
|
370
|
+
const button = item.cloneNode(true)
|
|
371
|
+
button.children[0].textContent = "link"
|
|
372
|
+
item.replaceWith(button)
|
|
373
|
+
button.onclick = addLink
|
|
374
|
+
const close = document.createElement("div")
|
|
375
|
+
close.style.cursor = "pointer"
|
|
376
|
+
close.style.textAlign = "center"
|
|
377
|
+
close.appendChild(document.createTextNode(gettext("(Press Alt+Enter to keep changes. / Alt+Q to cancel.)")))
|
|
378
|
+
close.title = gettext("Close this window.")
|
|
379
|
+
richtext.appendChild(close)
|
|
380
|
+
close.addEventListener("click", () => {
|
|
381
|
+
window.pluginSpreadsheet.closeEditor.call(richtext, td, true);
|
|
382
|
+
})
|
|
383
|
+
richtext.addEventListener("keydown", (event) => {
|
|
384
|
+
if(event.altKey && event.key == "Enter") {
|
|
385
|
+
window.pluginSpreadsheet.closeEditor.call(richtext, td, true)
|
|
386
|
+
} else if (event.altKey && event.key == "q") {
|
|
387
|
+
window.pluginSpreadsheet.closeEditor.call(richtext, td, false)
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
}, 100)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function getSpreadsheetConfig() {
|
|
396
|
+
const config = structuredClone(window.pluginSpreadsheet.getConfig())
|
|
397
|
+
delete config.data
|
|
398
|
+
// Remove default values.
|
|
399
|
+
const style = {}
|
|
400
|
+
for (const [key, value] of Object.entries(config.style)) {
|
|
401
|
+
if (value !== "text-align: center;") {
|
|
402
|
+
style[key] = value
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
config.style = style
|
|
406
|
+
return config
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function setSpreadsheetConfig(sheetConfig, path, sheetIsOpen) {
|
|
410
|
+
const configNode = document.querySelector("textarea[name=config]")
|
|
411
|
+
let config = {}
|
|
412
|
+
if (configNode.value) {
|
|
413
|
+
config = JSON.parse(configNode.value)
|
|
414
|
+
if (config === null) {
|
|
415
|
+
config = {}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
config[path] = sheetConfig
|
|
419
|
+
config.openedPath = sheetIsOpen ? path : null
|
|
420
|
+
configNode.value = JSON.stringify(config, null, 2)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function submitForm() {
|
|
424
|
+
const src = document.querySelector("textarea[name=context]")
|
|
425
|
+
const form = src.closest("form")
|
|
426
|
+
form.addEventListener('submit', (event) => {
|
|
427
|
+
if (window.pluginSpreadsheet) {
|
|
428
|
+
if (!saveSpreadsheetIntoFrom(true)) {
|
|
429
|
+
event.preventDefault()
|
|
430
|
+
return false
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
})
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function createLinkButton(label, title, path, data) {
|
|
437
|
+
const link = document.createElement("a")
|
|
438
|
+
link.classList.add("btn")
|
|
439
|
+
link.disabled = true
|
|
440
|
+
link.style.cursor = "pointer"
|
|
441
|
+
if (title) {
|
|
442
|
+
link.title = title
|
|
443
|
+
}
|
|
444
|
+
if (path) {
|
|
445
|
+
link.dataset.path = path
|
|
446
|
+
}
|
|
447
|
+
if (data) {
|
|
448
|
+
link.dataset.options = JSON.stringify(data)
|
|
449
|
+
}
|
|
450
|
+
link.appendChild(document.createTextNode(label))
|
|
451
|
+
return link
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function populateColumnsOptions(sheetConfig, tableOptions) {
|
|
455
|
+
for(let i = 0; i < tableOptions.types.length; i++) {
|
|
456
|
+
if (sheetConfig.columns[i] === undefined) {
|
|
457
|
+
sheetConfig.columns[i] = {type: tableOptions.types[i]}
|
|
458
|
+
}
|
|
459
|
+
if (sheetConfig.columns[i].title === undefined && tableOptions.titles && tableOptions.titles[i]) {
|
|
460
|
+
sheetConfig.columns[i].title = tableOptions.titles[i]
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function toggleSelected(element) {
|
|
466
|
+
const parent = element.closest("div")
|
|
467
|
+
for(const link of parent.querySelectorAll("a.selected")) {
|
|
468
|
+
link.classList.remove("selected")
|
|
469
|
+
}
|
|
470
|
+
element.classList.add("selected")
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
function openJsSheet(button) {
|
|
475
|
+
if (button.classList.contains("selected")) {
|
|
476
|
+
return
|
|
477
|
+
}
|
|
478
|
+
if (window.pluginSpreadsheet) {
|
|
479
|
+
if (!saveSpreadsheetIntoFrom(true)) {
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const mimetypeNone = document.querySelector("select[name=mimetype]")
|
|
484
|
+
button.dataset.mimetype = mimetypeNone.value
|
|
485
|
+
// Load table data and spreadsheet options.
|
|
486
|
+
let contextData, configOptions = {}
|
|
487
|
+
try {
|
|
488
|
+
contextData = getContextData(mimetypeNone.value)
|
|
489
|
+
} catch (error) {
|
|
490
|
+
displayError(error, gettext("Parse Data Error"))
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
const configValue = document.querySelector("textarea[name=config]").value
|
|
494
|
+
if (configValue) {
|
|
495
|
+
try {
|
|
496
|
+
configOptions = JSON.parse(configValue)
|
|
497
|
+
} catch (error) {
|
|
498
|
+
displayError(error, gettext("Error in Source settings"))
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
let payload
|
|
504
|
+
if (button.dataset.path !== ".") {
|
|
505
|
+
payload = getPayload(contextData, button.dataset.path)
|
|
506
|
+
} else {
|
|
507
|
+
payload = contextData
|
|
508
|
+
}
|
|
509
|
+
const [tableData, tableOptions] = convertDataToTableAndOptions(payload)
|
|
510
|
+
const path = button.dataset.path ? button.dataset.path : "."
|
|
511
|
+
let sheetConfig = (configOptions === null) ? null : configOptions[path]
|
|
512
|
+
if (sheetConfig) {
|
|
513
|
+
sheetConfig.data = tableData
|
|
514
|
+
} else {
|
|
515
|
+
sheetConfig = {minDimensions: [7, 10], data: tableData, columns: []}
|
|
516
|
+
}
|
|
517
|
+
populateColumnsOptions(sheetConfig, tableOptions)
|
|
518
|
+
createSpreadsheet(sheetConfig)
|
|
519
|
+
toggleSelected(button)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function createSpreadsheet(config) {
|
|
523
|
+
window.pluginSpreadsheet = window.jspreadsheet(document.getElementById(spreadsheetId), {
|
|
524
|
+
worksheets: [config],
|
|
525
|
+
oncreateeditor: onCreateEditor,
|
|
526
|
+
})[0]
|
|
527
|
+
document.querySelector("textarea[name=context]").style.display = "none"
|
|
528
|
+
document.querySelector("select[name=mimetype] option[value='application/vnd.oasis.opendocument.spreadsheet']").disabled = "disabled"
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function destroySpreadsheet() {
|
|
532
|
+
jspreadsheet.destroy(document.getElementById(spreadsheetId))
|
|
533
|
+
window.pluginSpreadsheet = null
|
|
534
|
+
document.querySelector("textarea[name=context]").style.display = ""
|
|
535
|
+
document.querySelector("select[name=mimetype] option[value='application/vnd.oasis.opendocument.spreadsheet']").disabled = ""
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function saveSpreadsheetIntoFrom(sheetIsOpen) {
|
|
539
|
+
const link = document.querySelector("#id-toggle-spreadsheet a.selected")
|
|
540
|
+
const mimetype = link.dataset.mimetype
|
|
541
|
+
let path = link.dataset.path
|
|
542
|
+
const options = link.dataset.options === undefined ? {} : JSON.parse(link.dataset.options)
|
|
543
|
+
const sheetData = removeEmptyRows(window.pluginSpreadsheet.getData())
|
|
544
|
+
let data
|
|
545
|
+
if (options.titles) {
|
|
546
|
+
data = convertDataIntoObjects(sheetData, options.titles)
|
|
547
|
+
} else {
|
|
548
|
+
data = sheetData
|
|
549
|
+
}
|
|
550
|
+
let contextData
|
|
551
|
+
if (path === ".") {
|
|
552
|
+
contextData = data
|
|
553
|
+
} else {
|
|
554
|
+
contextData = getContextData(mimetype)
|
|
555
|
+
setPayload(contextData, path, data)
|
|
556
|
+
}
|
|
557
|
+
try {
|
|
558
|
+
setDataIntoContext(contextData, {path: path, options: options})
|
|
559
|
+
} catch (error) {
|
|
560
|
+
displayError(error, gettext("Data serialization error"))
|
|
561
|
+
return false
|
|
562
|
+
}
|
|
563
|
+
const sheetConfig = getSpreadsheetConfig()
|
|
564
|
+
setSpreadsheetConfig(sheetConfig, path, sheetIsOpen)
|
|
565
|
+
destroySpreadsheet()
|
|
566
|
+
return true
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function toggleSource(event) {
|
|
570
|
+
if (event.target.classList.contains("selected")) {
|
|
571
|
+
return
|
|
572
|
+
}
|
|
573
|
+
if (saveSpreadsheetIntoFrom(false)) {
|
|
574
|
+
toggleSelected(event.target)
|
|
575
|
+
appendSpreadsheetButtons()
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function getColumnName(name, index) {
|
|
580
|
+
return name === undefined ? numberToColumn(index + 1) : name
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function getUniqueName(name, names) {
|
|
584
|
+
let uname = name, index = 1
|
|
585
|
+
while (names.includes(uname)) {
|
|
586
|
+
uname = `${name}${++index}`
|
|
587
|
+
}
|
|
588
|
+
return uname
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function convertDataIntoObjects(sheetData, titles) {
|
|
592
|
+
const data = []
|
|
593
|
+
for(let r=0; r < sheetData.length; r++) {
|
|
594
|
+
const line = {}
|
|
595
|
+
for(let c=0; c < sheetData[r].length; c++) {
|
|
596
|
+
const columnName = getUniqueName(getColumnName(titles[c], c), Object.keys(line))
|
|
597
|
+
line[columnName] = sheetData[r][c]
|
|
598
|
+
}
|
|
599
|
+
data.push(line)
|
|
600
|
+
}
|
|
601
|
+
return data
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function createDefaultButton(linkFrame) {
|
|
605
|
+
const link = createLinkButton(gettext("Edit CSV"), gettext("Show data in spreadsheet."), ".")
|
|
606
|
+
link.addEventListener("click", (event) => {
|
|
607
|
+
document.querySelector("select[name=mimetype]").value = "text/csv"
|
|
608
|
+
openJsSheet(event.target)
|
|
609
|
+
})
|
|
610
|
+
linkFrame.appendChild(link)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function appendSeparatorBetweenButtons(linkFrame) {
|
|
614
|
+
const label = document.createElement("span")
|
|
615
|
+
label.appendChild(document.createTextNode(gettext("Spreadsheet") + ":"))
|
|
616
|
+
linkFrame.appendChild(label)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function removeAllChildNodes(parent) {
|
|
620
|
+
while (parent.firstChild) {
|
|
621
|
+
parent.removeChild(parent.firstChild);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function appendSpreadsheetButtons() {
|
|
626
|
+
let linkFrame = document.querySelector("#id-toggle-spreadsheet")
|
|
627
|
+
if (linkFrame) {
|
|
628
|
+
removeAllChildNodes(linkFrame)
|
|
629
|
+
} else {
|
|
630
|
+
const context = document.querySelector("textarea[name=context]")
|
|
631
|
+
linkFrame = document.createElement("div")
|
|
632
|
+
linkFrame.id = "id-toggle-spreadsheet"
|
|
633
|
+
context.before(linkFrame)
|
|
634
|
+
}
|
|
635
|
+
const link = createLinkButton(gettext("Source"), gettext("View source data."))
|
|
636
|
+
link.classList.add("selected")
|
|
637
|
+
link.addEventListener("click", toggleSource)
|
|
638
|
+
linkFrame.appendChild(link)
|
|
639
|
+
let data
|
|
640
|
+
try {
|
|
641
|
+
data = getContextData(document.querySelector("select[name=mimetype]").value)
|
|
642
|
+
} catch (error) {
|
|
643
|
+
displayError(error, gettext("Parse Data Error"))
|
|
644
|
+
return
|
|
645
|
+
}
|
|
646
|
+
appendSeparatorBetweenButtons(linkFrame)
|
|
647
|
+
if (!data) {
|
|
648
|
+
createDefaultButton(linkFrame)
|
|
649
|
+
return
|
|
650
|
+
}
|
|
651
|
+
const paths = getPathsToArrays(data)
|
|
652
|
+
if (paths.length) {
|
|
653
|
+
for(let path of paths) {
|
|
654
|
+
const payload = getPayload(data, path)
|
|
655
|
+
const tableOptions = convertDataToTableAndOptions(payload)[1]
|
|
656
|
+
tableOptions.table = getTableName(path)
|
|
657
|
+
tableOptions.row = getTableRowName(path)
|
|
658
|
+
const link = createLinkButton(path, gettext('Show data in spreadsheet.'), path, tableOptions)
|
|
659
|
+
link.addEventListener("click", (event) => openJsSheet(event.target))
|
|
660
|
+
linkFrame.appendChild(link)
|
|
661
|
+
}
|
|
662
|
+
} else {
|
|
663
|
+
const tableOptions = convertDataToTableAndOptions(data)[1]
|
|
664
|
+
tableOptions.table = "table"
|
|
665
|
+
tableOptions.row = "row"
|
|
666
|
+
const link = createLinkButton(gettext("Display"), gettext('Show data in spreadsheet.'), ".", tableOptions)
|
|
667
|
+
link.addEventListener("click", (event) => openJsSheet(event.target))
|
|
668
|
+
linkFrame.appendChild(link)
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function openSpreadsheetIfRequired() {
|
|
673
|
+
const configValue = document.querySelector("textarea[name=config]").value
|
|
674
|
+
let config = {}
|
|
675
|
+
if (configValue) {
|
|
676
|
+
try {
|
|
677
|
+
config = JSON.parse(configValue)
|
|
678
|
+
} catch (error) {
|
|
679
|
+
displayError(error, gettext("Error in Source settings"))
|
|
680
|
+
return
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (config && config.openedPath && !window.pluginSpreadsheet) {
|
|
684
|
+
const button = document.querySelector(`#id-toggle-spreadsheet a[data-path="${config.openedPath}"]`)
|
|
685
|
+
if (button) {
|
|
686
|
+
openJsSheet(button)
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function handleMimetypeSelect() {
|
|
692
|
+
document.querySelector("select[name=mimetype]").addEventListener("change", (event) => {
|
|
693
|
+
document.getElementById("id-toggle-spreadsheet").disabled = event.target.value === "application/vnd.oasis.opendocument.spreadsheet" ? "disabled" : ""
|
|
694
|
+
})
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function sourceChanged() {
|
|
698
|
+
document.querySelector("textarea[name=context]").addEventListener("change", appendSpreadsheetButtons)
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
702
|
+
handleMimetypeSelect()
|
|
703
|
+
createSpreadsheetNode()
|
|
704
|
+
setSpreadsheetTranslations()
|
|
705
|
+
sourceChanged()
|
|
706
|
+
submitForm()
|
|
707
|
+
appendSpreadsheetButtons()
|
|
708
|
+
openSpreadsheetIfRequired()
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
})()
|