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.
@@ -0,0 +1,11 @@
1
+ let global = {
2
+ "state":{
3
+ },
4
+ "i18n":{
5
+ },
6
+ "settings":{
7
+ },
8
+ "user":{
9
+ },
10
+ }
11
+ export default global
@@ -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("&lt;", 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.0
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.0.dist-info/METADATA,sha256=I-6cYJ8MCIeqT-l5fbPmtkWLn8niwHFWPB0jYTkAGxo,1558
52
- vesta_web-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
- vesta_web-1.1.0.dist-info/entry_points.txt,sha256=_x509HUdPeKL_Fja0OqUuDa8zKGeLndMxIE_IfojlJg,51
54
- vesta_web-1.1.0.dist-info/licenses/LICENSE.md,sha256=zoPFEFUUoSgosmDBK5fGTWGRHHBaSVuuJT2ZQIYXuIk,177
55
- vesta_web-1.1.0.dist-info/RECORD,,
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,,