vesta-web 1.1.0__py3-none-any.whl → 1.1.1__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.
- vesta/emptyProject/static/framework/global.mjs +11 -0
- vesta/emptyProject/static/framework/navigation.mjs +295 -0
- vesta/emptyProject/static/framework/templating.mjs +110 -0
- vesta/emptyProject/static/framework/vesta.mjs +173 -0
- vesta/emptyProject/static/framework/websockets.mjs +41 -0
- vesta/emptyProject/static/markdown/markdown.mjs +463 -0
- vesta/emptyProject/static/markdown/utils.mjs +21 -0
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.1.dist-info}/METADATA +1 -1
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.1.dist-info}/RECORD +12 -5
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.1.dist-info}/WHEEL +0 -0
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.1.dist-info}/entry_points.txt +0 -0
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.1.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import global from "./global.mjs";
|
|
2
|
+
import {xhr} from "./templating.mjs";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Opens a menu by its DOM id. If the menu element exists, it is displayed. Otherwise, loads the menu template asynchronously.
|
|
7
|
+
* @param {string} id - The DOM id of the menu to open.
|
|
8
|
+
* @param {boolean} [async=true] - Whether to load the template asynchronously if not found.
|
|
9
|
+
*/
|
|
10
|
+
function openMenu(id, async=true){
|
|
11
|
+
const menu = document.getElementById(id)
|
|
12
|
+
if (menu){
|
|
13
|
+
menu.style.display = "flex"
|
|
14
|
+
}else{
|
|
15
|
+
loadTemplate(id.concat(".html"), undefined, id, async)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
window.openMenu = openMenu
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Toggles the display of a menu by its DOM id. If the menu exists, toggles its visibility. Otherwise, loads the menu template.
|
|
22
|
+
* @param {string} id - The DOM id of the menu to toggle.
|
|
23
|
+
* @param {boolean} [async=true] - Whether to load the template asynchronously if not found.
|
|
24
|
+
* @param {HTMLElement} [target=undefined] - Optional target HTML element for template loading.
|
|
25
|
+
*/
|
|
26
|
+
function toggleMenu(id, async=true,target=undefined){
|
|
27
|
+
const menu = document.getElementById(id)
|
|
28
|
+
if (menu){
|
|
29
|
+
if (menu.style.display === "flex"){
|
|
30
|
+
menu.style.display = "none"
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
menu.style.display = "flex"
|
|
34
|
+
}else{
|
|
35
|
+
loadTemplate(id.concat(".html"), target, id, async)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
window.toggleMenu = toggleMenu
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Navigates to a new view or section by loading a template and updating selection state.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} id - The id of the element to inject the template into.
|
|
44
|
+
* @param {string} target - The base name of the template to load (without .html).
|
|
45
|
+
* @param {Object} [selected=undefined] - Optional selection context {category | event | id} allowing to add/remove the "selected" class.
|
|
46
|
+
* @param {boolean} [async=true] - Whether to load the template asynchronously.
|
|
47
|
+
* @param {Function} [postInsert=undefined] - Optional callback to run after template insertion.
|
|
48
|
+
*
|
|
49
|
+
*/
|
|
50
|
+
export function goTo(id, target, selected=undefined, async=true, postInsert=undefined){
|
|
51
|
+
if (postInsert){
|
|
52
|
+
loadTemplate(target.concat(".html"), id, undefined, false)
|
|
53
|
+
postInsert()
|
|
54
|
+
}else{
|
|
55
|
+
loadTemplate(target.concat(".html"), id, undefined, async)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if(selected){
|
|
59
|
+
let targetElt;
|
|
60
|
+
if(global.state[selected.category]){
|
|
61
|
+
global.state[selected.category].classList.remove("selected")
|
|
62
|
+
}else if(selected.event){
|
|
63
|
+
selected.event.currentTarget.parentElement.querySelector('.selected').classList.remove("selected")
|
|
64
|
+
}else if(selected.id){
|
|
65
|
+
targetElt = document.getElementById(selected.id)
|
|
66
|
+
targetElt.parentElement.querySelector('.selected').classList.remove("selected")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if(selected.event){
|
|
70
|
+
global.state[selected.category] = selected.event.currentTarget
|
|
71
|
+
}else if(selected.id){
|
|
72
|
+
global.state[selected.category] = targetElt
|
|
73
|
+
}
|
|
74
|
+
global.state[selected.category].classList.add("selected")
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
window.goTo = goTo
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
let openingFM = false;
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Toggles the visibility of a floating menu (FM) by its DOM id.
|
|
85
|
+
* If another FM is open, it will be hidden and the new one will be shown.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} id - The DOM id of the floating menu to toggle.
|
|
88
|
+
*/
|
|
89
|
+
function toggleFM(id){
|
|
90
|
+
const FM = document.getElementById(id)
|
|
91
|
+
if(FM !== global.state.activeFM){
|
|
92
|
+
openingFM = true
|
|
93
|
+
if(global.state.activeFM){
|
|
94
|
+
global.state.activeFM.classList.toggle("visible")
|
|
95
|
+
}
|
|
96
|
+
global.state.activeFM = FM
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
window.toggleFM = toggleFM
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Closes the currently active floating menu (FM) if one is open.
|
|
103
|
+
*/
|
|
104
|
+
export function closeFM(){
|
|
105
|
+
if(global.state.activeFM){
|
|
106
|
+
global.state.activeFM.classList.toggle("visible")
|
|
107
|
+
global.state.activeFM = undefined
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Toggles the display of a modal window at the mouse event position.
|
|
113
|
+
* If another modal is open, it will be hidden and the new one will be shown at the event's coordinates.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} id - The DOM id of the modal to toggle.
|
|
116
|
+
* @param {Event} event - The event triggering the modal (used for positioning).
|
|
117
|
+
*/
|
|
118
|
+
function toggleModale(id, event){
|
|
119
|
+
const FM = document.getElementById(id)
|
|
120
|
+
if(FM !== global.state.activeFM){
|
|
121
|
+
openingFM = true
|
|
122
|
+
global.state.modaltarget = event.currentTarget
|
|
123
|
+
if(global.state.activeFM){
|
|
124
|
+
global.state.activeFM.classList.toggle("visible")
|
|
125
|
+
}
|
|
126
|
+
console.log(event.currentTarget,global.state.modaltarget)
|
|
127
|
+
FM.style.top = event.clientY.toString().concat("px")
|
|
128
|
+
FM.style.left = event.clientX.toString().concat("px")
|
|
129
|
+
global.state.activeFM = FM
|
|
130
|
+
if (event.type === "contextmenu"){
|
|
131
|
+
event.preventDefault()
|
|
132
|
+
global.state.activeFM.classList.add("visible")
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
window.toggleModale = toggleModale
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Toggles the closed/open state of a group element (e.g., collapsible section).
|
|
140
|
+
*/
|
|
141
|
+
function toggleGroup(){
|
|
142
|
+
event.currentTarget.classList.toggle("closed")
|
|
143
|
+
}
|
|
144
|
+
window.toggleGroup = toggleGroup
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Navigates to a specific step in a multi-step menu by translating the menu horizontally.
|
|
148
|
+
*
|
|
149
|
+
* @param {number} step - The step index to navigate to.
|
|
150
|
+
* @param {string} stepsID - The DOM id of the steps container.
|
|
151
|
+
*/
|
|
152
|
+
function gotoStep(step,stepsID){
|
|
153
|
+
const menu = document.getElementById(stepsID)
|
|
154
|
+
menu.style.transform=`translateX(${-100*step/menu.childElementCount}%)`
|
|
155
|
+
}
|
|
156
|
+
window.gotoStep = gotoStep
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Removes the 'selected' class from all elements with class 'selected' inside the given container.
|
|
160
|
+
* Used for resetting selection state in multi-step menus.
|
|
161
|
+
*
|
|
162
|
+
* @param {string} id - The DOM id of the container to reset selection in.
|
|
163
|
+
*/
|
|
164
|
+
function resetSelected(id){
|
|
165
|
+
const selected = document.getElementById(id).querySelectorAll(".selected")
|
|
166
|
+
for(let el of selected){
|
|
167
|
+
el.classList.remove("selected")
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Closes a menu by hiding it and resetting its state. Can target a specific element or use the event target.
|
|
173
|
+
*
|
|
174
|
+
* @param {string|undefined} cible - CSS selector of the element to close, or undefined to use event target.
|
|
175
|
+
* @param {string} [id='createServerSteps'] - The DOM id of the steps container to reset.
|
|
176
|
+
*
|
|
177
|
+
*/
|
|
178
|
+
function closeMenu(cible = undefined,id='createServerSteps'){
|
|
179
|
+
if (global.state.disableClose){
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if(!cible){
|
|
184
|
+
if(event.target !== event.currentTarget){
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
event.target.style.display = "none";
|
|
188
|
+
}else{
|
|
189
|
+
const cibleEl= document.querySelector(cible)
|
|
190
|
+
cibleEl.style.display = "none";
|
|
191
|
+
}
|
|
192
|
+
gotoStep(0,id)
|
|
193
|
+
resetSelected(id)
|
|
194
|
+
}
|
|
195
|
+
window.closeMenu = closeMenu
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Saves the current cursor position in a text input or textarea to global state.
|
|
199
|
+
* Used to paste emojis in a textarea from an emoji-keyboard.
|
|
200
|
+
*
|
|
201
|
+
* @param {Event} event - The input event containing the cursor position.
|
|
202
|
+
*/
|
|
203
|
+
function saveCursorPosition(event){
|
|
204
|
+
global.state.previousCursor={start:event.srcElement.selectionStart, end:event.srcElement.selectionEnd}
|
|
205
|
+
}
|
|
206
|
+
window.saveCursorPosition = saveCursorPosition
|
|
207
|
+
|
|
208
|
+
function isMobileDevice() {
|
|
209
|
+
return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function initNavigation(){
|
|
213
|
+
window.global = global
|
|
214
|
+
global.state.dom = document.querySelector("body")
|
|
215
|
+
|
|
216
|
+
const { hostname, port } = window.location;
|
|
217
|
+
global.state.location = { host:hostname, port:port, short:port ? hostname + ":" + port : hostname }
|
|
218
|
+
|
|
219
|
+
global.state.isMobile = isMobileDevice()
|
|
220
|
+
if (global.state.isMobile) {
|
|
221
|
+
global.state.dom.classList.add("mobile")
|
|
222
|
+
|
|
223
|
+
const onload = function(){
|
|
224
|
+
console.log(this.responseText)
|
|
225
|
+
}
|
|
226
|
+
xhr("/static/mobileUiManifest.mjs", onload)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
document.addEventListener('click', function (event) {
|
|
230
|
+
if (global.state.activeFM && !global.state.activeFM.contains(event.target)) {
|
|
231
|
+
global.state.activeFM.classList.toggle("visible")
|
|
232
|
+
if (!openingFM){
|
|
233
|
+
global.state.activeFM = undefined
|
|
234
|
+
}else{
|
|
235
|
+
openingFM=false
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}, false);
|
|
239
|
+
|
|
240
|
+
if (global.state.socket){
|
|
241
|
+
window.addEventListener('unload', () => {
|
|
242
|
+
if (global.state?.socket.readyState !== WebSocket.CLOSED) {
|
|
243
|
+
const message = {"type" : 'unregister', "clientID":global.state.clientID}
|
|
244
|
+
global.state.socket.send(JSON.stringify(message))
|
|
245
|
+
global.state.socket.close()
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
function print(...args){
|
|
254
|
+
const green ="#68f66e"
|
|
255
|
+
const blue ="#4284f5"
|
|
256
|
+
const yellow ="#fef972"
|
|
257
|
+
const red ="#ee6966"
|
|
258
|
+
const pink ="#fb7bfa"
|
|
259
|
+
const purple ="#6a76fa"
|
|
260
|
+
|
|
261
|
+
const colors = [green,blue,green,"white",green,blue,"white",yellow,blue,"white",yellow,"white",yellow,red,pink,purple];
|
|
262
|
+
console.log(`%c${args.join(' ')}`, ...colors.map(c => `color: ${c};`));
|
|
263
|
+
// console.log(colors.map(c => `%c${c}`).join(''), ...colors.map(c => `background: ${c};`));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
export function printWatermark(name, git){
|
|
268
|
+
print(` %c ${name}\n` +
|
|
269
|
+
"%c⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ %c -----------------------------------\n" +
|
|
270
|
+
"%c⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ %c Credits%c: Lou ! \n" +
|
|
271
|
+
`%c⠀⠀⠀⠀⠀⠀⠀⠸⣷⣦⣀⠀⠀⠀⠀⠀⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀ %c Git%c: ${git}\n` +
|
|
272
|
+
"%c⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣦⠀⠠⠾⠿⣿⣷⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀\n" +
|
|
273
|
+
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠟⢉⣠⣤⣶⡆⠀⣠⣈⠀⢀⣠⣴⣿⣿⠋⠀⠀⠀⠀ %c Powered by Vesta ! \n" +
|
|
274
|
+
"%c⠀⢀⡀⢀⣀⣀⣠⣤⡄⢀⣀⡘⣿⣿⣿⣷⣼⣿⣿⣷⡄⠹⣿⡿⠁⠀⠀⠀⠀⠀\n" +
|
|
275
|
+
"%c⠀⠀⠻⠿⢿⣿⣿⣿⠁⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣁⠀⠋⠀⠀⠀⠀⠀⠀⠀ \n" +
|
|
276
|
+
"⠀⠀⠀⠀⠀⠀⠈⠻⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢰⣄⣀⠀⠀⠀⠀⠀\n" +
|
|
277
|
+
"%c⠀⠀ ⠀⠀⠀⠀⣠⡀⠀⣴⣿⣿⣿⣿⣿⣿⣿⡿⢿⡿⠀⣾⣿⣿⣿⣿⣶⡄⠀ \n" +
|
|
278
|
+
"⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠻⣿⣿⡿⠻⣿⣿⣿⣿⠀⠀⠈⠉⠉⠉⠀⠀⠀⠀⠀\n" +
|
|
279
|
+
"⠀⠀⠀⠀⣠⣾⡿⠟⠉⠉⠀⢀⡉⠁⠀⠛⠛⢉⣠⣴⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀\n" +
|
|
280
|
+
"%c⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠀⢸⣿⣿⡿⠉⠀⠙⠿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀\n" +
|
|
281
|
+
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠙⠿⣷⠀⠀⠀⠀⠀⠀⠀\n" +
|
|
282
|
+
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀")
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function hideLoadingScreen() {
|
|
286
|
+
const loadingScreen = document.getElementById('loading-screen');
|
|
287
|
+
|
|
288
|
+
// Fade out the loading screen
|
|
289
|
+
loadingScreen.classList.add('fade-out');
|
|
290
|
+
|
|
291
|
+
// Optional: wait until the transition ends before removing or showing content
|
|
292
|
+
loadingScreen.addEventListener('transitionend', () => {
|
|
293
|
+
loadingScreen.style.display = 'none';
|
|
294
|
+
});
|
|
295
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
let templates = {}
|
|
2
|
+
|
|
3
|
+
export function xhr(endpoint,effect,method="GET", async=true, body=undefined){
|
|
4
|
+
let xhr= new XMLHttpRequest();
|
|
5
|
+
xhr.open(method, endpoint, async);
|
|
6
|
+
xhr.onload=effect
|
|
7
|
+
xhr.onerror = function() {
|
|
8
|
+
console.log("request failed")
|
|
9
|
+
};
|
|
10
|
+
if (body) {
|
|
11
|
+
xhr.setRequestHeader("Content-Type", "application/json");
|
|
12
|
+
xhr.send(JSON.stringify(body));
|
|
13
|
+
}else{
|
|
14
|
+
xhr.send();
|
|
15
|
+
}
|
|
16
|
+
return xhr
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function loadTemplate(template, target=undefined, flex= undefined, async){
|
|
21
|
+
const effect = function() {
|
|
22
|
+
if (target){
|
|
23
|
+
//maybe not using eval
|
|
24
|
+
document.getElementById(target).innerHTML = eval('`' + this.responseText + '`');
|
|
25
|
+
}else{
|
|
26
|
+
global.state.dom.insertAdjacentHTML('beforeend',eval('`' + this.responseText + '`'))
|
|
27
|
+
}
|
|
28
|
+
if (flex){
|
|
29
|
+
document.getElementById(flex).style.display = "flex"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
xhr( '/static/templates/'.concat(template), effect,"GET", async)
|
|
34
|
+
}
|
|
35
|
+
window.loadTemplate = loadTemplate
|
|
36
|
+
|
|
37
|
+
function fillWith(template, list){
|
|
38
|
+
// console.log("fillWith",template,list,typeof list)
|
|
39
|
+
|
|
40
|
+
let request
|
|
41
|
+
if(templates[template]){
|
|
42
|
+
request={"responseText":templates[template]}
|
|
43
|
+
}else{
|
|
44
|
+
request = xhr( '/static/templates/'.concat(template,".html"), undefined, "GET", false)
|
|
45
|
+
// console.log("adding to cache",templates, template)
|
|
46
|
+
templates[template] = request.responseText
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let content = ""
|
|
50
|
+
if (typeof list == 'object'){
|
|
51
|
+
for (let elementId in list){
|
|
52
|
+
const element = list[elementId]
|
|
53
|
+
content += eval('`' + request.responseText + '`')
|
|
54
|
+
}
|
|
55
|
+
}else if (!list){
|
|
56
|
+
console.warn("fillWith called with undefined list")
|
|
57
|
+
return content
|
|
58
|
+
}else{
|
|
59
|
+
for (let element of list){
|
|
60
|
+
content += eval('`' + request.responseText + '`')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return content
|
|
65
|
+
}
|
|
66
|
+
window.fillWith = fillWith
|
|
67
|
+
|
|
68
|
+
function Subscribe(element, content, className=undefined, params=undefined){
|
|
69
|
+
//subscribe content to element, content will be reevaluated on element change
|
|
70
|
+
const domElement = document.createElement('div')
|
|
71
|
+
domElement.className = element.replaceAll('.','-').replaceAll('[','🪟').replaceAll(']','🥹')
|
|
72
|
+
|
|
73
|
+
if (className){
|
|
74
|
+
const classList = className.split(" ")
|
|
75
|
+
for (let name of classList){
|
|
76
|
+
domElement.classList.add(name)
|
|
77
|
+
}
|
|
78
|
+
domElement.dataset.defaultClass = domElement.className
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (params?.target === "class"){
|
|
82
|
+
domElement.dataset.target = params.target
|
|
83
|
+
domElement.className = domElement.className + " " + content()
|
|
84
|
+
}else if(params?.target === "style"){
|
|
85
|
+
domElement.dataset.target = params.target
|
|
86
|
+
domElement.style = content()
|
|
87
|
+
} else{
|
|
88
|
+
domElement.innerHTML = content()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if (params?.element){
|
|
94
|
+
domElement.dataset.element = params.element
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if(params?.repaint){
|
|
98
|
+
domElement.dataset.repaint = params.repaint
|
|
99
|
+
}else{
|
|
100
|
+
domElement.dataset.content = content
|
|
101
|
+
}
|
|
102
|
+
return domElement.outerHTML
|
|
103
|
+
}
|
|
104
|
+
window.Subscribe = Subscribe
|
|
105
|
+
|
|
106
|
+
function getTemplate(name){
|
|
107
|
+
const request = xhr( '/static/templates/'.concat(name,".html"), ()=>{}, "GET", false)
|
|
108
|
+
return eval('`' + request.responseText + '`')
|
|
109
|
+
}
|
|
110
|
+
window.getTemplate = getTemplate
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
function logout() {
|
|
2
|
+
const url = "/logout";
|
|
3
|
+
fetch(url, {
|
|
4
|
+
method: 'POST',
|
|
5
|
+
redirect: 'manual'
|
|
6
|
+
})
|
|
7
|
+
.then(response => {
|
|
8
|
+
const location = response.headers.get('location');
|
|
9
|
+
if (response.status === 302 && location) {
|
|
10
|
+
window.location.href = location;
|
|
11
|
+
} else {
|
|
12
|
+
// fallback: reload page or handle error
|
|
13
|
+
window.location.reload();
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.catch(() => {
|
|
17
|
+
console.log("request failed");
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
window.logout = logout
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export function updateElement(selector,value=undefined){
|
|
25
|
+
const subscriptions = document.querySelectorAll(`[class^="${selector.replaceAll('.','-').replaceAll('[','🪟').replaceAll(']','🥹')}"]`)
|
|
26
|
+
for (let sub of subscriptions){
|
|
27
|
+
if(sub.dataset.repaint){
|
|
28
|
+
const fn = eval(sub.dataset.repaint)
|
|
29
|
+
fn()
|
|
30
|
+
}else if(sub.dataset.target === "class"){
|
|
31
|
+
|
|
32
|
+
const content = eval(sub.dataset.content)
|
|
33
|
+
if(sub.dataset.element){
|
|
34
|
+
sub.className = sub.dataset.defaultClass.concat(' ',content(eval(sub.dataset.element)))
|
|
35
|
+
}else{
|
|
36
|
+
sub.className = sub.dataset.defaultClass.concat(' ',content())
|
|
37
|
+
}
|
|
38
|
+
}else if(sub.dataset.target === "style"){
|
|
39
|
+
const content = eval(sub.dataset.content)
|
|
40
|
+
if(sub.dataset.element){
|
|
41
|
+
sub.style = content(eval(sub.dataset.element))
|
|
42
|
+
}else{
|
|
43
|
+
sub.style = content()
|
|
44
|
+
}
|
|
45
|
+
}else{
|
|
46
|
+
|
|
47
|
+
const content = eval(sub.dataset.content)
|
|
48
|
+
if(sub.dataset.element){
|
|
49
|
+
sub.innerHTML = content(eval(sub.dataset.element))
|
|
50
|
+
}else{
|
|
51
|
+
sub.innerHTML = content()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function setElement(element, value){
|
|
58
|
+
// console.log(element,'has been updated to:', value);
|
|
59
|
+
eval(`${element} = value`);
|
|
60
|
+
updateElement(element,value)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function pushElement(element, value){
|
|
64
|
+
// console.log(element,'has been added:', value);
|
|
65
|
+
eval(`${element}.push(value)`);
|
|
66
|
+
updateElement(element,value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function addElement(element, value){
|
|
70
|
+
// console.log(element,'has been added:', value);
|
|
71
|
+
eval(`${element}[value.id] = value`);
|
|
72
|
+
updateElement(element,value)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// this deletes an element from a list BY ID
|
|
76
|
+
export function deleteElement(element, id){
|
|
77
|
+
// console.log(element,'has been removed:', element[id]);
|
|
78
|
+
eval(`${element}.splice(id,1)`);
|
|
79
|
+
updateElement(element)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// this deletes an element from a dict BY ID
|
|
83
|
+
export function deleteElementDict(element, id){
|
|
84
|
+
// console.log(element,'has been removed:', element[id]);
|
|
85
|
+
eval(`delete ${element}[id]`);
|
|
86
|
+
updateElement(element)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// this deletes an element from a list BY VALUE
|
|
90
|
+
export function deleteVal(element, val){
|
|
91
|
+
// console.log(element,'has been removed:', element[id]);
|
|
92
|
+
eval(`${element} = ${element}.filter(item => item !== val)`);
|
|
93
|
+
updateElement(element)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function checkEnter(event, effect){
|
|
97
|
+
if (event.key === "Enter"){
|
|
98
|
+
effect()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
window.checkEnter = checkEnter
|
|
102
|
+
|
|
103
|
+
function getTimeStr(timestamp, options = { locale: "fr-FR" }) {
|
|
104
|
+
const date = new Date(timestamp);
|
|
105
|
+
|
|
106
|
+
const defaultOptions = {
|
|
107
|
+
year: "numeric",
|
|
108
|
+
month: "numeric",
|
|
109
|
+
day: "numeric",
|
|
110
|
+
hour: "numeric",
|
|
111
|
+
minute: "numeric",
|
|
112
|
+
hour12: false,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
116
|
+
// console.log("TIMESTR",date.toLocaleDateString(undefined, mergedOptions))
|
|
117
|
+
return date.toLocaleDateString(undefined, mergedOptions);
|
|
118
|
+
}
|
|
119
|
+
window.getTimeStr = getTimeStr
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
function goodbye(){
|
|
123
|
+
if (confirm('Voulez-vous vraiment vous désinscrire et supprimer votre compte de tous les services Carbonlab ? (toutes vos informations seront effacées)')) {
|
|
124
|
+
const url = "/goodbye";
|
|
125
|
+
let request = new XMLHttpRequest();
|
|
126
|
+
request.open('POST', url, true);
|
|
127
|
+
request.onload = function() { // request successful
|
|
128
|
+
console.log("account deleted")
|
|
129
|
+
|
|
130
|
+
if (request.response === 302){
|
|
131
|
+
window.location.href = request.response.headers.get('location');
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
request.onerror = function() {
|
|
136
|
+
console.log("request failed")
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
request.send();
|
|
140
|
+
} else {
|
|
141
|
+
console.log("ouf 😖")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
window.goodbye = goodbye
|
|
145
|
+
|
|
146
|
+
export function difference(arrKeys, dict) {
|
|
147
|
+
const result = [];
|
|
148
|
+
const dictKeys = new Set(Object.keys(dict)); // Convert dict keys to a set for efficient lookup
|
|
149
|
+
|
|
150
|
+
for (const key of arrKeys) {
|
|
151
|
+
if (!dictKeys.has(key.toString())) {
|
|
152
|
+
result.push(key);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return Array.from(result);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function generateUUID() {
|
|
159
|
+
let d = new Date().getTime();//Timestamp
|
|
160
|
+
let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
|
|
161
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
162
|
+
let r = Math.random() * 16;//random number between 0 and 16
|
|
163
|
+
if(d > 0){//Use timestamp until depleted
|
|
164
|
+
r = (d + r)%16 | 0;
|
|
165
|
+
d = Math.floor(d/16);
|
|
166
|
+
} else {//Use microseconds since page-load if supported
|
|
167
|
+
r = (d2 + r)%16 | 0;
|
|
168
|
+
d2 = Math.floor(d2/16);
|
|
169
|
+
}
|
|
170
|
+
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import global from "./global.mjs";
|
|
2
|
+
import {onMessage} from "../ws/onMessage.mjs";
|
|
3
|
+
import {WEBSOCKETS} from "/config";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export function initWebSockets(){
|
|
7
|
+
global.state.websocket = new WebSocket(WEBSOCKETS);
|
|
8
|
+
|
|
9
|
+
global.state.websocket.onopen = function(event) {
|
|
10
|
+
if (!global.user.id) {
|
|
11
|
+
const cookie = document.cookie.split('; ').find(row => row.startsWith('client' + '='));
|
|
12
|
+
if (cookie) {
|
|
13
|
+
global.user['id'] = cookie.split('=')[1];
|
|
14
|
+
} else {
|
|
15
|
+
console.log("ERROR : INVALID SESSION",document);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
console.log("Connection opened to Python WebSocket server!");
|
|
21
|
+
const message = {"type" : 'register', "uid": global.user.id};
|
|
22
|
+
global.state.websocket.send(JSON.stringify(message));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
global.state.websocket.onmessage = onMessage;
|
|
26
|
+
|
|
27
|
+
global.state.websocket.onclose = function(event) {
|
|
28
|
+
console.log(`[WS] Connexion closed - Code: ${event.code}, Reason: ${event.reason}`);
|
|
29
|
+
|
|
30
|
+
if (event.code !== 1000 && event.code !== 1001) {
|
|
31
|
+
console.log("[WS] Trying to reconnect in 3 secondes...");
|
|
32
|
+
setTimeout(() => {
|
|
33
|
+
initWebSockets();
|
|
34
|
+
}, 3000);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
global.state.websocket.onerror = function(error) {
|
|
39
|
+
console.error("WebSocket error:", error);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
// This in absolutely no spec, it comes exclusively from my deranged mind
|
|
2
|
+
|
|
3
|
+
export class Markdown {
|
|
4
|
+
constructor() {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
HTML_equiv = {
|
|
8
|
+
"#":"<h${props.level}>${content}</h${props.level}>",
|
|
9
|
+
"text":"${content}",
|
|
10
|
+
"start li":"<li>${content}</li>",
|
|
11
|
+
"*":"<i>${content}</i>",
|
|
12
|
+
"**":"<b>${content}</b>",
|
|
13
|
+
">":"<div class='answer'>${content}</div>",
|
|
14
|
+
"'''":"<div class='code-wrapper'><code>${content}</code><div class='circle grey' onclick='copyCode(event)'></div></div>",
|
|
15
|
+
"~~":"<div class='crossed'>${content}</div>",
|
|
16
|
+
"||":"<div class='spoiler' onclick='showSpoiler(event)'>${content}</div>",
|
|
17
|
+
"link":"<a href='${props.link}' target='_blank'>${content}</a>",
|
|
18
|
+
"color":"<div class='color' style='color: ${props.color}'>${content}</div>",
|
|
19
|
+
"endline":"\n",
|
|
20
|
+
"newline":"",
|
|
21
|
+
")":")",
|
|
22
|
+
"'":"'",
|
|
23
|
+
"(":"(",
|
|
24
|
+
"/>":"/>",
|
|
25
|
+
"]":"]",
|
|
26
|
+
"/":"${content}/"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
tokenize(str) {
|
|
30
|
+
const tokenList = []
|
|
31
|
+
let currentToken = new Token("newline")
|
|
32
|
+
let commitEndline = false
|
|
33
|
+
|
|
34
|
+
let char_id=0
|
|
35
|
+
while (char_id < str.length ){
|
|
36
|
+
|
|
37
|
+
//look for url
|
|
38
|
+
if(str.slice(char_id,char_id+6) === "https:" || str.slice(char_id,char_id+5) === "http:" ){
|
|
39
|
+
if (currentToken.content !== ""){
|
|
40
|
+
tokenList.push(currentToken)
|
|
41
|
+
currentToken = new Token("link")
|
|
42
|
+
}else{
|
|
43
|
+
currentToken.type="link"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let look_id = 5
|
|
47
|
+
let nextToken
|
|
48
|
+
while (char_id+look_id<str.length){
|
|
49
|
+
if(str[char_id+look_id]=== "\n"){
|
|
50
|
+
nextToken = new Token("newline")
|
|
51
|
+
look_id++
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
if([" ",")","]"].includes(str[char_id+look_id])){
|
|
55
|
+
nextToken = new Token("text")
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
look_id++
|
|
59
|
+
}
|
|
60
|
+
currentToken.content = str.slice(char_id,char_id+look_id)
|
|
61
|
+
currentToken.props.link = currentToken.content
|
|
62
|
+
tokenList.push(currentToken)
|
|
63
|
+
if(char_id+look_id>=str.length){
|
|
64
|
+
return tokenList;
|
|
65
|
+
}
|
|
66
|
+
char_id += look_id
|
|
67
|
+
currentToken = nextToken
|
|
68
|
+
}else{
|
|
69
|
+
const char = str[char_id]
|
|
70
|
+
switch (currentToken.type){
|
|
71
|
+
case "text":
|
|
72
|
+
switch (char){
|
|
73
|
+
case '*':
|
|
74
|
+
if (currentToken.content !== ""){
|
|
75
|
+
tokenList.push(currentToken)
|
|
76
|
+
}
|
|
77
|
+
currentToken = new Token("*")
|
|
78
|
+
currentToken.props.level = 1
|
|
79
|
+
break
|
|
80
|
+
case '[':
|
|
81
|
+
case ']':
|
|
82
|
+
case '(':
|
|
83
|
+
case ')':
|
|
84
|
+
if (currentToken.content !== ""){
|
|
85
|
+
tokenList.push(currentToken)
|
|
86
|
+
}
|
|
87
|
+
tokenList.push(new Token(char))
|
|
88
|
+
currentToken = new Token("text")
|
|
89
|
+
break
|
|
90
|
+
case '|':
|
|
91
|
+
case '~':
|
|
92
|
+
case "'":
|
|
93
|
+
case '/':
|
|
94
|
+
case '&':
|
|
95
|
+
case '<':
|
|
96
|
+
currentToken.type = char
|
|
97
|
+
break
|
|
98
|
+
case '\n':
|
|
99
|
+
if (commitEndline){
|
|
100
|
+
commitEndline = false
|
|
101
|
+
tokenList.push(currentToken)
|
|
102
|
+
tokenList.push(new Token("endline"))
|
|
103
|
+
currentToken = new Token("newline")
|
|
104
|
+
}else{
|
|
105
|
+
currentToken.content = currentToken.content.concat(char)
|
|
106
|
+
currentToken.props.consuming="text"
|
|
107
|
+
currentToken.type="newline"
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
default:
|
|
111
|
+
currentToken.content = currentToken.content.concat(char)
|
|
112
|
+
}
|
|
113
|
+
break
|
|
114
|
+
case "#":
|
|
115
|
+
switch (char){
|
|
116
|
+
case '#':
|
|
117
|
+
currentToken.props.level = currentToken.props.level<3 ? currentToken.props.level+1 : 3
|
|
118
|
+
break
|
|
119
|
+
case '\n':
|
|
120
|
+
tokenList.push(currentToken)
|
|
121
|
+
currentToken = new Token("newline")
|
|
122
|
+
break
|
|
123
|
+
default:
|
|
124
|
+
currentToken.content = currentToken.content.concat(char)
|
|
125
|
+
}
|
|
126
|
+
break
|
|
127
|
+
case "newline":
|
|
128
|
+
switch (char){
|
|
129
|
+
case '#':
|
|
130
|
+
if (currentToken.content.trim() !== ""){
|
|
131
|
+
currentToken.type=currentToken.props.consuming
|
|
132
|
+
tokenList.push(currentToken)
|
|
133
|
+
}
|
|
134
|
+
currentToken = new Token("#")
|
|
135
|
+
currentToken.props["level"] = 1
|
|
136
|
+
break
|
|
137
|
+
case '-':
|
|
138
|
+
if (currentToken.content.trim() !== ""){
|
|
139
|
+
currentToken.type=currentToken.props.consuming
|
|
140
|
+
tokenList.push(currentToken)
|
|
141
|
+
}
|
|
142
|
+
currentToken = new Token("text")
|
|
143
|
+
tokenList.push(new Token("start li"))
|
|
144
|
+
commitEndline = true
|
|
145
|
+
break
|
|
146
|
+
case '>':
|
|
147
|
+
if (currentToken.content.trim() !== ""){
|
|
148
|
+
currentToken.type=currentToken.props.consuming
|
|
149
|
+
tokenList.push(currentToken)
|
|
150
|
+
}
|
|
151
|
+
currentToken = new Token("text")
|
|
152
|
+
tokenList.push(new Token(">"))
|
|
153
|
+
commitEndline = true
|
|
154
|
+
break
|
|
155
|
+
case '*':
|
|
156
|
+
currentToken = new Token("*")
|
|
157
|
+
currentToken.props.level = 1
|
|
158
|
+
break
|
|
159
|
+
case '|':
|
|
160
|
+
case '~':
|
|
161
|
+
case "'":
|
|
162
|
+
case '/':
|
|
163
|
+
case '&':
|
|
164
|
+
case '<':
|
|
165
|
+
currentToken.type = char
|
|
166
|
+
break
|
|
167
|
+
case '[':
|
|
168
|
+
case ']':
|
|
169
|
+
case '(':
|
|
170
|
+
case ')':
|
|
171
|
+
tokenList.push(new Token(char))
|
|
172
|
+
currentToken = new Token("text")
|
|
173
|
+
break
|
|
174
|
+
case ' ':
|
|
175
|
+
currentToken.content = currentToken.content.concat(char)
|
|
176
|
+
break
|
|
177
|
+
case '\n':
|
|
178
|
+
currentToken.type = "text"
|
|
179
|
+
tokenList.push(currentToken)
|
|
180
|
+
tokenList.push(new Token("endline"))
|
|
181
|
+
currentToken = new Token("newline")
|
|
182
|
+
break
|
|
183
|
+
default:
|
|
184
|
+
currentToken.type="text"
|
|
185
|
+
currentToken.content = currentToken.content.concat(char)
|
|
186
|
+
}
|
|
187
|
+
break
|
|
188
|
+
case "*":
|
|
189
|
+
switch (char){
|
|
190
|
+
case "*":
|
|
191
|
+
currentToken.props.level = currentToken.props.level<3 ? currentToken.props.level+1 : 3
|
|
192
|
+
break
|
|
193
|
+
case '\n':
|
|
194
|
+
tokenList.push(currentToken)
|
|
195
|
+
currentToken = new Token("newline")
|
|
196
|
+
currentToken.content = "\n"
|
|
197
|
+
if (commitEndline) {
|
|
198
|
+
commitEndline = false
|
|
199
|
+
}
|
|
200
|
+
break
|
|
201
|
+
case '|':
|
|
202
|
+
case '~':
|
|
203
|
+
case '/':
|
|
204
|
+
tokenList.push(currentToken)
|
|
205
|
+
currentToken = new Token(char)
|
|
206
|
+
break
|
|
207
|
+
default:
|
|
208
|
+
tokenList.push(currentToken)
|
|
209
|
+
currentToken = new Token("text")
|
|
210
|
+
currentToken.content = currentToken.content.concat(char)
|
|
211
|
+
}
|
|
212
|
+
break
|
|
213
|
+
case '|':
|
|
214
|
+
switch (char){
|
|
215
|
+
case "|":
|
|
216
|
+
if (currentToken.content !== ""){
|
|
217
|
+
currentToken.type = "text"
|
|
218
|
+
tokenList.push(currentToken)
|
|
219
|
+
}
|
|
220
|
+
currentToken = new Token("||")
|
|
221
|
+
tokenList.push(currentToken)
|
|
222
|
+
currentToken = new Token("text")
|
|
223
|
+
break
|
|
224
|
+
default:
|
|
225
|
+
currentToken.type="text"
|
|
226
|
+
currentToken.content="|".concat(char)
|
|
227
|
+
}
|
|
228
|
+
break
|
|
229
|
+
case '~':
|
|
230
|
+
switch (char){
|
|
231
|
+
case "~":
|
|
232
|
+
if (currentToken.content !== ""){
|
|
233
|
+
currentToken.type = "text"
|
|
234
|
+
tokenList.push(currentToken)
|
|
235
|
+
}
|
|
236
|
+
currentToken = new Token("~~")
|
|
237
|
+
tokenList.push(currentToken)
|
|
238
|
+
currentToken = new Token("text")
|
|
239
|
+
break
|
|
240
|
+
default:
|
|
241
|
+
currentToken.type="text"
|
|
242
|
+
currentToken.content="~".concat(char)
|
|
243
|
+
}
|
|
244
|
+
break
|
|
245
|
+
case "'":
|
|
246
|
+
switch (char){
|
|
247
|
+
case "'":
|
|
248
|
+
if(!currentToken.props.level || currentToken.props.level<2){
|
|
249
|
+
currentToken.props.level = currentToken.props.level ? currentToken.props.level+1 : 2
|
|
250
|
+
}else{
|
|
251
|
+
currentToken = new Token("'''")
|
|
252
|
+
currentToken.props.level=0
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
default:
|
|
256
|
+
currentToken.type = "text"
|
|
257
|
+
currentToken.content = currentToken.content.concat("'",char)
|
|
258
|
+
}
|
|
259
|
+
break
|
|
260
|
+
case "'''":
|
|
261
|
+
switch (char){
|
|
262
|
+
case "'":
|
|
263
|
+
if(currentToken.props.level<2){
|
|
264
|
+
currentToken.props.level += 1
|
|
265
|
+
}else{
|
|
266
|
+
tokenList.push(currentToken)
|
|
267
|
+
currentToken = new Token("text")
|
|
268
|
+
}
|
|
269
|
+
break
|
|
270
|
+
default:
|
|
271
|
+
if(currentToken.props.level !==0){
|
|
272
|
+
currentToken.content = currentToken.content.concat("'".repeat(currentToken.props.level))
|
|
273
|
+
currentToken.props.level = 0
|
|
274
|
+
}
|
|
275
|
+
currentToken.content = currentToken.content.concat(char)
|
|
276
|
+
}
|
|
277
|
+
break
|
|
278
|
+
case "&":
|
|
279
|
+
if(str.slice(char_id, char_id+3) === "lt;"){
|
|
280
|
+
if (currentToken.content !== ""){
|
|
281
|
+
currentToken.type = "text"
|
|
282
|
+
tokenList.push(currentToken)
|
|
283
|
+
}
|
|
284
|
+
char_id+=2
|
|
285
|
+
currentToken = new Token("<")
|
|
286
|
+
}else{
|
|
287
|
+
currentToken.content = currentToken.content.concat(char)
|
|
288
|
+
currentToken.type = "text"
|
|
289
|
+
}
|
|
290
|
+
break
|
|
291
|
+
case "<":
|
|
292
|
+
switch (char){
|
|
293
|
+
case "$":
|
|
294
|
+
if (currentToken.content !== ""){
|
|
295
|
+
currentToken.type = "text"
|
|
296
|
+
tokenList.push(currentToken)
|
|
297
|
+
}
|
|
298
|
+
currentToken = new Token("color")
|
|
299
|
+
currentToken.props.color=""
|
|
300
|
+
break
|
|
301
|
+
default:
|
|
302
|
+
currentToken.type = "text"
|
|
303
|
+
currentToken.content = currentToken.content.concat("<", char)
|
|
304
|
+
}
|
|
305
|
+
break
|
|
306
|
+
case "color":
|
|
307
|
+
switch (char){
|
|
308
|
+
case " ":
|
|
309
|
+
tokenList.push(currentToken)
|
|
310
|
+
currentToken = new Token("text")
|
|
311
|
+
break
|
|
312
|
+
case "\n":
|
|
313
|
+
tokenList.push(currentToken)
|
|
314
|
+
currentToken = new Token("newline")
|
|
315
|
+
break
|
|
316
|
+
default:
|
|
317
|
+
currentToken.props.color = currentToken.props.color.concat(char)
|
|
318
|
+
}
|
|
319
|
+
break
|
|
320
|
+
case "/":
|
|
321
|
+
switch (char){
|
|
322
|
+
case '>':
|
|
323
|
+
if (currentToken.content !== ""){
|
|
324
|
+
currentToken.type = "text"
|
|
325
|
+
tokenList.push(currentToken)
|
|
326
|
+
}
|
|
327
|
+
currentToken = new Token("/>")
|
|
328
|
+
tokenList.push(currentToken)
|
|
329
|
+
currentToken = new Token("text")
|
|
330
|
+
break
|
|
331
|
+
default:
|
|
332
|
+
currentToken.type = "text"
|
|
333
|
+
currentToken.content = currentToken.content.concat("/", char)
|
|
334
|
+
}
|
|
335
|
+
break
|
|
336
|
+
}
|
|
337
|
+
char_id += 1
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
tokenList.push(currentToken)
|
|
341
|
+
return tokenList;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
render(tokens) {
|
|
345
|
+
let result = ""
|
|
346
|
+
for(let token_id = 0; token_id < tokens.length; token_id+=1){
|
|
347
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
348
|
+
token_id = render[1]
|
|
349
|
+
result = result.concat(render[0])
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
renderToken(token, token_id, tokens){
|
|
355
|
+
// console.log("render token",token,token_id,tokens)
|
|
356
|
+
const old_id = token_id
|
|
357
|
+
let content = ""
|
|
358
|
+
switch(token.type) {
|
|
359
|
+
case ">":
|
|
360
|
+
case "start li":
|
|
361
|
+
while (token_id + 1 < tokens.length && tokens[token_id + 1].type !== "endline") {
|
|
362
|
+
token_id += 1
|
|
363
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
364
|
+
token_id = render[1]
|
|
365
|
+
content = content.concat(render[0])
|
|
366
|
+
}
|
|
367
|
+
token.content = content
|
|
368
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id+1]
|
|
369
|
+
case "*":
|
|
370
|
+
// this is suboptimal because we're looking for same size closing
|
|
371
|
+
// smth like *** a ** b * will not work as intended
|
|
372
|
+
while (token_id + 1 < tokens.length && tokens[token_id + 1].type !== "endline") {
|
|
373
|
+
token_id += 1
|
|
374
|
+
if(tokens[token_id].type === "*" && tokens[token_id].props.level === token.props.level){
|
|
375
|
+
token.content = content
|
|
376
|
+
if (token.props.level % 2) {
|
|
377
|
+
if (token.props.level === 3) {
|
|
378
|
+
token.content = fillTemplate(this.HTML_equiv["**"], token)
|
|
379
|
+
}
|
|
380
|
+
return [fillTemplate(this.HTML_equiv["*"], token), token_id]
|
|
381
|
+
} else {
|
|
382
|
+
return [fillTemplate(this.HTML_equiv["**"], token), token_id]
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
386
|
+
token_id = render[1]
|
|
387
|
+
content = content.concat(render[0])
|
|
388
|
+
}
|
|
389
|
+
token.content = "*".repeat(token.props.level)
|
|
390
|
+
// should be optimized, because we just drop a part of the render here
|
|
391
|
+
return [fillTemplate(this.HTML_equiv["text"], token), old_id]
|
|
392
|
+
case "~~":
|
|
393
|
+
case "||":
|
|
394
|
+
while (token_id + 1 < tokens.length) {
|
|
395
|
+
token_id += 1
|
|
396
|
+
if(tokens[token_id].type === token.type){
|
|
397
|
+
token.content = content
|
|
398
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
399
|
+
}
|
|
400
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
401
|
+
token_id = render[1]
|
|
402
|
+
content = content.concat(render[0])
|
|
403
|
+
}
|
|
404
|
+
token.content = token.type
|
|
405
|
+
return [fillTemplate(this.HTML_equiv["text"], token), old_id]
|
|
406
|
+
case "[":
|
|
407
|
+
while (token_id + 1 < tokens.length) {
|
|
408
|
+
token_id += 1
|
|
409
|
+
if(tokens[token_id].type === "\n"){
|
|
410
|
+
break
|
|
411
|
+
}
|
|
412
|
+
if(tokens[token_id].type === "]"){
|
|
413
|
+
if(tokens[token_id+1].type === "(" && tokens[token_id+2].type === "link" && tokens[token_id+3].type === ")"){
|
|
414
|
+
token.content = content
|
|
415
|
+
token.props.link = tokens[token_id+2].content
|
|
416
|
+
return [fillTemplate(this.HTML_equiv["link"], token), token_id+3]
|
|
417
|
+
}else{
|
|
418
|
+
break
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
422
|
+
token_id = render[1]
|
|
423
|
+
content = content.concat(render[0])
|
|
424
|
+
}
|
|
425
|
+
token.content = token.type
|
|
426
|
+
return [fillTemplate(this.HTML_equiv["text"], token), old_id]
|
|
427
|
+
case "color":
|
|
428
|
+
while (token_id + 1 < tokens.length) {
|
|
429
|
+
token_id += 1
|
|
430
|
+
if(tokens[token_id].type === "/>"){
|
|
431
|
+
token.content = content
|
|
432
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
433
|
+
}
|
|
434
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
435
|
+
token_id = render[1]
|
|
436
|
+
content = content.concat(render[0])
|
|
437
|
+
}
|
|
438
|
+
token.type="text"
|
|
439
|
+
token.content = "<$"+token.props?.color
|
|
440
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
441
|
+
default:
|
|
442
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
class Token{
|
|
448
|
+
constructor(type) {
|
|
449
|
+
this.type = type
|
|
450
|
+
this.content=""
|
|
451
|
+
this.props={}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const fillTemplate = (template, vars = {}) => {
|
|
456
|
+
const handler = new Function('vars', [
|
|
457
|
+
'const tagged = ( ' + Object.keys(vars).join(', ') + ' ) =>',
|
|
458
|
+
'`' + template + '`',
|
|
459
|
+
'return tagged(...Object.values(vars))'
|
|
460
|
+
].join('\n'));
|
|
461
|
+
const res = handler(vars)
|
|
462
|
+
return res;
|
|
463
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {Markdown} from "./markdown.mjs";
|
|
2
|
+
|
|
3
|
+
export function MDToHTML(text){
|
|
4
|
+
const engine = new Markdown()
|
|
5
|
+
const tokens = engine.tokenize(text)
|
|
6
|
+
console.log("text",text,"tokens",tokens)
|
|
7
|
+
return engine.render(tokens)
|
|
8
|
+
}
|
|
9
|
+
window.MDToHTML = MDToHTML
|
|
10
|
+
|
|
11
|
+
export function showSpoiler(event){
|
|
12
|
+
event.currentTarget.classList.toggle("clicked")
|
|
13
|
+
}
|
|
14
|
+
window.showSpoiler = showSpoiler
|
|
15
|
+
|
|
16
|
+
export function copyCode(event){
|
|
17
|
+
navigator.clipboard.writeText(event.currentTarget.parentElement.firstElementChild.innerHTML).then(
|
|
18
|
+
event.currentTarget.style.background = "var(--green)"
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
window.copyCode = copyCode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vesta-web
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: An extensive web framework adding every feature needed for Carbonlab
|
|
5
5
|
Project-URL: Homepage, https://gitlab.com/Louciole/vesta
|
|
6
6
|
Project-URL: Issues, https://gitlab.com/Louciole/vesta/-/issues
|
|
@@ -28,11 +28,18 @@ vesta/emptyProject/static/main.html,sha256=zsQF82JQ67Tms9AEPW9tZLVClVTlVX2uPGPiL
|
|
|
28
28
|
vesta/emptyProject/static/main.mjs,sha256=1j5antaPx8aUhuULg0YEMH3zNuJQyWxKTHUcGbQiyBc,396
|
|
29
29
|
vesta/emptyProject/static/mobileUiManifest.mjs,sha256=IwSGmWykI_KHASJ-IzvOkQUP8mmT8_J8BIVa1ro0aso,30
|
|
30
30
|
vesta/emptyProject/static/style.css,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
vesta/emptyProject/static/framework/global.mjs,sha256=Zr3F9E_DfyYzxqYfdeSBCTezf42VNUqkWR1zB3Py_Tw,124
|
|
32
|
+
vesta/emptyProject/static/framework/navigation.mjs,sha256=EMI-bTJDp0XqTWxHQkZKyT-u5Q9cKA6QVkF4hJyKrRY,11119
|
|
33
|
+
vesta/emptyProject/static/framework/templating.mjs,sha256=v0w-JtkOoSgHWAy7_A24jZzvBQ99f5FBii2RrykUAkU,3319
|
|
34
|
+
vesta/emptyProject/static/framework/vesta.mjs,sha256=0YrLyt4US22TIknO3OFWeOQuWimIWLBwMFCoKzBEHX4,5459
|
|
35
|
+
vesta/emptyProject/static/framework/websockets.mjs,sha256=Ck0bdEj0mVU3PHZ1MtPPz2He47OYl2WmRxqE3NSKh-c,1349
|
|
31
36
|
vesta/emptyProject/static/home/auth.css,sha256=Vw-wFyQJm1ydDVBHPdKWp4dvzrfFRry4-kgScVFleOM,1924
|
|
32
37
|
vesta/emptyProject/static/home/auth.html,sha256=sz-CC-B4NUqth8aSQqwQCvq2V46AnhaIIcGHRU6Wyl8,1836
|
|
33
38
|
vesta/emptyProject/static/home/auth.js,sha256=txMlUqr4A-15Czf4MYwaED35XXcaHI_zrc4SpnjItJU,4622
|
|
34
39
|
vesta/emptyProject/static/home/reset.html,sha256=EcXfX2Vil3NrYhDvEptrIhW9QV9X_kDy59-TK2S_P4w,2655
|
|
35
40
|
vesta/emptyProject/static/home/verif.html,sha256=tUw96l0FZ5-AXuE8s-7v1nM632onErq5swgCU53zP-s,2906
|
|
41
|
+
vesta/emptyProject/static/markdown/markdown.mjs,sha256=gpSUS-8UUJYb_sahgnrobqMvtlYpS2Q-AaE-RUeqIyA,21469
|
|
42
|
+
vesta/emptyProject/static/markdown/utils.mjs,sha256=PGxhsJfub_lt-LEZVqYt8lKEp_RTRVGXuNhGmWxto-k,614
|
|
36
43
|
vesta/emptyProject/static/translations/en.mjs,sha256=ouMluPVTgB4Q5vmb7zGE6YGTH4URruog5_a52sBDYNE,22
|
|
37
44
|
vesta/emptyProject/static/translations/fr.mjs,sha256=ouMluPVTgB4Q5vmb7zGE6YGTH4URruog5_a52sBDYNE,22
|
|
38
45
|
vesta/emptyProject/static/translations/translation.mjs,sha256=JxJ2peSlYVQK-bUKpfddPLXm0XZiz2yu6A6iWIqpKyM,1422
|
|
@@ -48,8 +55,8 @@ vesta/scripts/install.py,sha256=GvH_HHx5aU5_54RQ1_2vz4DaLCh42AHfUKy-m0q21vY,2125
|
|
|
48
55
|
vesta/scripts/testsRun.py,sha256=PQkxKyCwM-TGu9KbLSIkz70o5jnGQSf5aFN9Gil3_1U,2459
|
|
49
56
|
vesta/scripts/utils.py,sha256=MQZ29b4eplF0OR9EimUToOO73CVoV_cTxQeez2F3OoY,3460
|
|
50
57
|
vesta/scripts/vesta.py,sha256=xfY-dVzPAbJdmgpRJoGGNSlSXDsMyrQPwykAK017YqE,7506
|
|
51
|
-
vesta_web-1.1.
|
|
52
|
-
vesta_web-1.1.
|
|
53
|
-
vesta_web-1.1.
|
|
54
|
-
vesta_web-1.1.
|
|
55
|
-
vesta_web-1.1.
|
|
58
|
+
vesta_web-1.1.1.dist-info/METADATA,sha256=avBj6zSXwT0FDvjBkpk36sMrjF2hLChFn8-TEB_4RC0,1558
|
|
59
|
+
vesta_web-1.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
60
|
+
vesta_web-1.1.1.dist-info/entry_points.txt,sha256=_x509HUdPeKL_Fja0OqUuDa8zKGeLndMxIE_IfojlJg,51
|
|
61
|
+
vesta_web-1.1.1.dist-info/licenses/LICENSE.md,sha256=zoPFEFUUoSgosmDBK5fGTWGRHHBaSVuuJT2ZQIYXuIk,177
|
|
62
|
+
vesta_web-1.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|