mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	Format with biome instead of standard
This commit is contained in:
		| @@ -8,10 +8,11 @@ repos: | |||||||
|         args: ["--fix", "--silent"] |         args: ["--fix", "--silent"] | ||||||
|       # Run the formatter. |       # Run the formatter. | ||||||
|       - id: ruff-format |       - id: ruff-format | ||||||
|   - repo: https://github.com/standard/standard |   - repo: https://github.com/biomejs/pre-commit | ||||||
|     rev: v17.1.2 |     rev: "v0.1.0"  # Use the sha / tag you want to point at | ||||||
|     hooks: |     hooks: | ||||||
|       - id: standard |       - id: biome-check | ||||||
|  |         additional_dependencies: ["@biomejs/biome@1.9.3"] | ||||||
|   - repo: https://github.com/rtts/djhtml |   - repo: https://github.com/rtts/djhtml | ||||||
|     rev: 3.0.6 |     rev: 3.0.6 | ||||||
|     hooks: |     hooks: | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								biome.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								biome.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | { | ||||||
|  | 	"$schema": "./node_modules/@biomejs/biome/configuration_schema.json", | ||||||
|  | 	"vcs": { | ||||||
|  | 		"enabled": true, | ||||||
|  | 		"clientKind": "git", | ||||||
|  | 		"useIgnoreFile": true | ||||||
|  | 	}, | ||||||
|  | 	"files": { | ||||||
|  | 		"ignoreUnknown": false, | ||||||
|  | 		"ignore": ["core/static/vendored", "*.min.js", "staticfiles/generated"] | ||||||
|  | 	}, | ||||||
|  | 	"formatter": { | ||||||
|  | 		"enabled": true, | ||||||
|  | 		"indentStyle": "tab" | ||||||
|  | 	}, | ||||||
|  | 	"organizeImports": { | ||||||
|  | 		"enabled": true | ||||||
|  | 	}, | ||||||
|  | 	"linter": { | ||||||
|  | 		"enabled": true, | ||||||
|  | 		"ignore": ["core/static/vendored", "*.min.js", "staticfiles/generated"], | ||||||
|  | 		"rules": { | ||||||
|  | 			"recommended": true | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"javascript": { | ||||||
|  | 		"formatter": { | ||||||
|  | 			"quoteStyle": "double" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,20 +1,23 @@ | |||||||
| $(document).ready(function () { | $(document).ready(() => { | ||||||
|   $('#poster_list #view').click(function (e) { | 	$("#poster_list #view").click((e) => { | ||||||
|     $('#view').removeClass('active') | 		$("#view").removeClass("active"); | ||||||
|   }) | 	}); | ||||||
|  |  | ||||||
|   $('#poster_list .poster .image').click(function (e) { | 	$("#poster_list .poster .image").click((e) => { | ||||||
|     let el = $(e.target) | 		let el = $(e.target); | ||||||
|     if (el.hasClass('image')) { el = el.find('img') } | 		if (el.hasClass("image")) { | ||||||
|     $('#poster_list #view #placeholder').html(el.clone()) | 			el = el.find("img"); | ||||||
|  |  | ||||||
|     $('#view').addClass('active') |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   $(document).keyup(function (e) { |  | ||||||
|     if (e.keyCode === 27) { // escape key maps to keycode `27` |  | ||||||
|       e.preventDefault() |  | ||||||
|       $('#view').removeClass('active') |  | ||||||
| 		} | 		} | ||||||
|   }) | 		$("#poster_list #view #placeholder").html(el.clone()); | ||||||
| }) |  | ||||||
|  | 		$("#view").addClass("active"); | ||||||
|  | 	}); | ||||||
|  |  | ||||||
|  | 	$(document).keyup((e) => { | ||||||
|  | 		if (e.keyCode === 27) { | ||||||
|  | 			// escape key maps to keycode `27` | ||||||
|  | 			e.preventDefault(); | ||||||
|  | 			$("#view").removeClass("active"); | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|   | |||||||
| @@ -1,104 +1,106 @@ | |||||||
| // TODO: remove disable | $(document).ready(() => { | ||||||
| /* eslint-disable no-undef, camelcase */ | 	transition_time = 1000; | ||||||
| $(document).ready(function () { |  | ||||||
|   transition_time = 1000 |  | ||||||
|  |  | ||||||
|   i = 0 | 	i = 0; | ||||||
|   max = $('#slideshow .slide').length | 	max = $("#slideshow .slide").length; | ||||||
|  |  | ||||||
|   next_trigger = 0 | 	next_trigger = 0; | ||||||
|  |  | ||||||
|   function enterFullscreen () { | 	function enterFullscreen() { | ||||||
|     element = document.getElementById('slideshow') | 		element = document.getElementById("slideshow"); | ||||||
|     $(element).addClass('fullscreen') | 		$(element).addClass("fullscreen"); | ||||||
| 		if (element.requestFullscreen) { | 		if (element.requestFullscreen) { | ||||||
|       element.requestFullscreen() | 			element.requestFullscreen(); | ||||||
| 		} else if (element.mozRequestFullScreen) { | 		} else if (element.mozRequestFullScreen) { | ||||||
|       element.mozRequestFullScreen() | 			element.mozRequestFullScreen(); | ||||||
| 		} else if (element.webkitRequestFullscreen) { | 		} else if (element.webkitRequestFullscreen) { | ||||||
|       element.webkitRequestFullscreen() | 			element.webkitRequestFullscreen(); | ||||||
| 		} else if (element.msRequestFullscreen) { | 		} else if (element.msRequestFullscreen) { | ||||||
|       element.msRequestFullscreen() | 			element.msRequestFullscreen(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   function exitFullscreen () { | 	function exitFullscreen() { | ||||||
|     element = document.getElementById('slideshow') | 		element = document.getElementById("slideshow"); | ||||||
|     $(element).removeClass('fullscreen') | 		$(element).removeClass("fullscreen"); | ||||||
| 		if (document.exitFullscreen) { | 		if (document.exitFullscreen) { | ||||||
|       document.exitFullscreen() | 			document.exitFullscreen(); | ||||||
| 		} else if (document.webkitExitFullscreen) { | 		} else if (document.webkitExitFullscreen) { | ||||||
|       document.webkitExitFullscreen() | 			document.webkitExitFullscreen(); | ||||||
| 		} else if (document.mozCancelFullScreen) { | 		} else if (document.mozCancelFullScreen) { | ||||||
|       document.mozCancelFullScreen() | 			document.mozCancelFullScreen(); | ||||||
| 		} else if (document.msExitFullscreen) { | 		} else if (document.msExitFullscreen) { | ||||||
|       document.msExitFullscreen() | 			document.msExitFullscreen(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   function init_progress_bar () { | 	function init_progress_bar() { | ||||||
|     $('#slideshow #progress_bar').css('transition', 'none') | 		$("#slideshow #progress_bar").css("transition", "none"); | ||||||
|     $('#slideshow #progress_bar').removeClass('progress') | 		$("#slideshow #progress_bar").removeClass("progress"); | ||||||
|     $('#slideshow #progress_bar').addClass('init') | 		$("#slideshow #progress_bar").addClass("init"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   function start_progress_bar (display_time) { | 	function start_progress_bar(display_time) { | ||||||
|     $('#slideshow #progress_bar').removeClass('init') | 		$("#slideshow #progress_bar").removeClass("init"); | ||||||
|     $('#slideshow #progress_bar').addClass('progress') | 		$("#slideshow #progress_bar").addClass("progress"); | ||||||
|     $('#slideshow #progress_bar').css('transition', 'width ' + display_time + 's linear') | 		$("#slideshow #progress_bar").css( | ||||||
|  | 			"transition", | ||||||
|  | 			`width ${display_time}s linear`, | ||||||
|  | 		); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   function next () { | 	function next() { | ||||||
|     init_progress_bar() | 		init_progress_bar(); | ||||||
|     slide = $($('#slideshow .slide').get(i % max)) | 		slide = $($("#slideshow .slide").get(i % max)); | ||||||
|     slide.removeClass('center') | 		slide.removeClass("center"); | ||||||
|     slide.addClass('left') | 		slide.addClass("left"); | ||||||
|  |  | ||||||
|     next_slide = $($('#slideshow .slide').get((i + 1) % max)) | 		next_slide = $($("#slideshow .slide").get((i + 1) % max)); | ||||||
|     next_slide.removeClass('right') | 		next_slide.removeClass("right"); | ||||||
|     next_slide.addClass('center') | 		next_slide.addClass("center"); | ||||||
|     display_time = next_slide.attr('display_time') || 2 | 		display_time = next_slide.attr("display_time") || 2; | ||||||
|  |  | ||||||
|     $('#slideshow .bullet').removeClass('active') | 		$("#slideshow .bullet").removeClass("active"); | ||||||
|     bullet = $('#slideshow .bullet')[(i + 1) % max] | 		bullet = $("#slideshow .bullet")[(i + 1) % max]; | ||||||
|     $(bullet).addClass('active') | 		$(bullet).addClass("active"); | ||||||
|  |  | ||||||
|     i = (i + 1) % max | 		i = (i + 1) % max; | ||||||
|  |  | ||||||
|     setTimeout(function () { | 		setTimeout(() => { | ||||||
|       others_left = $('#slideshow .slide.left') | 			others_left = $("#slideshow .slide.left"); | ||||||
|       others_left.removeClass('left') | 			others_left.removeClass("left"); | ||||||
|       others_left.addClass('right') | 			others_left.addClass("right"); | ||||||
|  |  | ||||||
|       start_progress_bar(display_time) | 			start_progress_bar(display_time); | ||||||
|       next_trigger = setTimeout(next, display_time * 1000) | 			next_trigger = setTimeout(next, display_time * 1000); | ||||||
|     }, transition_time) | 		}, transition_time); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   display_time = $('#slideshow .center').attr('display_time') | 	display_time = $("#slideshow .center").attr("display_time"); | ||||||
|   init_progress_bar() | 	init_progress_bar(); | ||||||
|   setTimeout(function () { | 	setTimeout(() => { | ||||||
| 		if (max > 1) { | 		if (max > 1) { | ||||||
|       start_progress_bar(display_time) | 			start_progress_bar(display_time); | ||||||
|       setTimeout(next, display_time * 1000) | 			setTimeout(next, display_time * 1000); | ||||||
| 		} | 		} | ||||||
|   }, 10) | 	}, 10); | ||||||
|  |  | ||||||
|   $('#slideshow').click(function (e) { | 	$("#slideshow").click((e) => { | ||||||
|     if (!$('#slideshow').hasClass('fullscreen')) { | 		if (!$("#slideshow").hasClass("fullscreen")) { | ||||||
|       console.log('Entering fullscreen ...') | 			console.log("Entering fullscreen ..."); | ||||||
|       enterFullscreen() | 			enterFullscreen(); | ||||||
| 		} else { | 		} else { | ||||||
|       console.log('Exiting fullscreen ...') | 			console.log("Exiting fullscreen ..."); | ||||||
|       exitFullscreen() | 			exitFullscreen(); | ||||||
| 		} | 		} | ||||||
|   }) | 	}); | ||||||
|  |  | ||||||
|   $(document).keyup(function (e) { | 	$(document).keyup((e) => { | ||||||
|     if (e.keyCode === 27) { // escape key maps to keycode `27` | 		if (e.keyCode === 27) { | ||||||
|       e.preventDefault() | 			// escape key maps to keycode `27` | ||||||
|       console.log('Exiting fullscreen ...') | 			e.preventDefault(); | ||||||
|       exitFullscreen() | 			console.log("Exiting fullscreen ..."); | ||||||
|  | 			exitFullscreen(); | ||||||
| 		} | 		} | ||||||
|   }) | 	}); | ||||||
| }) | }); | ||||||
|   | |||||||
| @@ -1 +1,14 @@ | |||||||
| [{"pk": 1, "fields": {"permissions": [1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12, 19, 20, 21, 22, 25, 23, 24, 16, 17, 18, 13, 14, 15]}, "model": "core.group"}, {"pk": 2, "fields": {"permissions": [25]}, "model": "core.group"}, {"pk": 3, "fields": {"permissions": []}, "model": "core.group"}] | [ | ||||||
|  | 	{ | ||||||
|  | 		"pk": 1, | ||||||
|  | 		"fields": { | ||||||
|  | 			"permissions": [ | ||||||
|  | 				1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12, 19, 20, 21, 22, 25, 23, 24, 16, | ||||||
|  | 				17, 18, 13, 14, 15 | ||||||
|  | 			] | ||||||
|  | 		}, | ||||||
|  | 		"model": "core.group" | ||||||
|  | 	}, | ||||||
|  | 	{ "pk": 2, "fields": { "permissions": [25] }, "model": "core.group" }, | ||||||
|  | 	{ "pk": 3, "fields": { "permissions": [] }, "model": "core.group" } | ||||||
|  | ] | ||||||
|   | |||||||
| @@ -1 +1,74 @@ | |||||||
| [{"pk": 1, "model": "core.page", "fields": {"full_name": "guy2", "owner_group": 1, "parent": null, "edit_groups": [], "name": "guy2", "view_groups": []}}, {"pk": 2, "model": "core.page", "fields": {"full_name": "guy2/bibou", "owner_group": 1, "parent": 1, "edit_group": [], "name": "bibou", "view_group": []}}, {"pk": 3, "model": "core.page", "fields": {"full_name": "guy2/bibou/troll", "owner_group": 1, "parent": 2, "edit_group": [], "name": "troll", "view_group": []}}, {"pk": 4, "model": "core.page", "fields": {"full_name": "guy", "owner_group": 1, "parent": null, "edit_group": [1], "name": "guy", "view_group": [1]}}, {"pk": 5, "model": "core.page", "fields": {"full_name": "bibou", "owner_group": 3, "parent": null, "edit_group": [1], "name": "bibou", "view_group": []}}, {"pk": 6, "model": "core.page", "fields": {"full_name": "guy2/guy", "owner_group": 1, "parent": 1, "edit_group": [], "name": "guy", "view_group": []}}] | [ | ||||||
|  | 	{ | ||||||
|  | 		"pk": 1, | ||||||
|  | 		"model": "core.page", | ||||||
|  | 		"fields": { | ||||||
|  | 			"full_name": "guy2", | ||||||
|  | 			"owner_group": 1, | ||||||
|  | 			"parent": null, | ||||||
|  | 			"edit_groups": [], | ||||||
|  | 			"name": "guy2", | ||||||
|  | 			"view_groups": [] | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"pk": 2, | ||||||
|  | 		"model": "core.page", | ||||||
|  | 		"fields": { | ||||||
|  | 			"full_name": "guy2/bibou", | ||||||
|  | 			"owner_group": 1, | ||||||
|  | 			"parent": 1, | ||||||
|  | 			"edit_group": [], | ||||||
|  | 			"name": "bibou", | ||||||
|  | 			"view_group": [] | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"pk": 3, | ||||||
|  | 		"model": "core.page", | ||||||
|  | 		"fields": { | ||||||
|  | 			"full_name": "guy2/bibou/troll", | ||||||
|  | 			"owner_group": 1, | ||||||
|  | 			"parent": 2, | ||||||
|  | 			"edit_group": [], | ||||||
|  | 			"name": "troll", | ||||||
|  | 			"view_group": [] | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"pk": 4, | ||||||
|  | 		"model": "core.page", | ||||||
|  | 		"fields": { | ||||||
|  | 			"full_name": "guy", | ||||||
|  | 			"owner_group": 1, | ||||||
|  | 			"parent": null, | ||||||
|  | 			"edit_group": [1], | ||||||
|  | 			"name": "guy", | ||||||
|  | 			"view_group": [1] | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"pk": 5, | ||||||
|  | 		"model": "core.page", | ||||||
|  | 		"fields": { | ||||||
|  | 			"full_name": "bibou", | ||||||
|  | 			"owner_group": 3, | ||||||
|  | 			"parent": null, | ||||||
|  | 			"edit_group": [1], | ||||||
|  | 			"name": "bibou", | ||||||
|  | 			"view_group": [] | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"pk": 6, | ||||||
|  | 		"model": "core.page", | ||||||
|  | 		"fields": { | ||||||
|  | 			"full_name": "guy2/guy", | ||||||
|  | 			"owner_group": 1, | ||||||
|  | 			"parent": 1, | ||||||
|  | 			"edit_group": [], | ||||||
|  | 			"name": "guy", | ||||||
|  | 			"view_group": [] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | ] | ||||||
|   | |||||||
| @@ -1 +1,42 @@ | |||||||
| [{"model": "core.user", "pk": 1, "fields": {"first_name": "Ro", "date_joined": "2015-11-19T16:05:51.764Z", "groups": [], "password": "pbkdf2_sha256$20000$MDukCN5X8Bof$rYdhppKiusj+W/1Rxpy0yuYsEyWocESEjtRsopkOc5c=", "last_name": "Ot", "nick_name": "", "username": "root", "user_permissions": [], "email": "bibou@git.an", "last_login": "2015-11-26T16:28:36.464Z", "date_of_birth": "1969-12-31T23:00:00Z", "is_superuser": true, "is_active": true, "is_staff": true}}, {"model": "core.user", "pk": 2, "fields": {"first_name": "Skia", "date_joined": "2015-11-19T16:06:29.556Z", "groups": [3], "password": "pbkdf2_sha256$20000$UK9a29p5bBEh$Jzv7xs0W9njJZiXfIdYXDydim/3YHs6awKwDmN7gSAc=", "last_name": "Kia", "nick_name": "", "username": "skia", "user_permissions": [], "email": "plop@libskia.so", "last_login": "2015-11-26T16:37:01.671Z", "date_of_birth": "1969-12-31T23:00:00Z", "is_superuser": false, "is_active": true, "is_staff": false}}] | [ | ||||||
|  | 	{ | ||||||
|  | 		"model": "core.user", | ||||||
|  | 		"pk": 1, | ||||||
|  | 		"fields": { | ||||||
|  | 			"first_name": "Ro", | ||||||
|  | 			"date_joined": "2015-11-19T16:05:51.764Z", | ||||||
|  | 			"groups": [], | ||||||
|  | 			"password": "pbkdf2_sha256$20000$MDukCN5X8Bof$rYdhppKiusj+W/1Rxpy0yuYsEyWocESEjtRsopkOc5c=", | ||||||
|  | 			"last_name": "Ot", | ||||||
|  | 			"nick_name": "", | ||||||
|  | 			"username": "root", | ||||||
|  | 			"user_permissions": [], | ||||||
|  | 			"email": "bibou@git.an", | ||||||
|  | 			"last_login": "2015-11-26T16:28:36.464Z", | ||||||
|  | 			"date_of_birth": "1969-12-31T23:00:00Z", | ||||||
|  | 			"is_superuser": true, | ||||||
|  | 			"is_active": true, | ||||||
|  | 			"is_staff": true | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"model": "core.user", | ||||||
|  | 		"pk": 2, | ||||||
|  | 		"fields": { | ||||||
|  | 			"first_name": "Skia", | ||||||
|  | 			"date_joined": "2015-11-19T16:06:29.556Z", | ||||||
|  | 			"groups": [3], | ||||||
|  | 			"password": "pbkdf2_sha256$20000$UK9a29p5bBEh$Jzv7xs0W9njJZiXfIdYXDydim/3YHs6awKwDmN7gSAc=", | ||||||
|  | 			"last_name": "Kia", | ||||||
|  | 			"nick_name": "", | ||||||
|  | 			"username": "skia", | ||||||
|  | 			"user_permissions": [], | ||||||
|  | 			"email": "plop@libskia.so", | ||||||
|  | 			"last_login": "2015-11-26T16:37:01.671Z", | ||||||
|  | 			"date_of_birth": "1969-12-31T23:00:00Z", | ||||||
|  | 			"is_superuser": false, | ||||||
|  | 			"is_active": true, | ||||||
|  | 			"is_staff": false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | ] | ||||||
|   | |||||||
| @@ -1,42 +1,121 @@ | |||||||
| /*--------------------------------RESET--------------------------------*/ | /*--------------------------------RESET--------------------------------*/ | ||||||
| /*--------------------------------RESET--------------------------------*/ | /*--------------------------------RESET--------------------------------*/ | ||||||
| html, body, div, span, applet, object, iframe, | html, | ||||||
| h1, h2, h3, h4, h5, h6, p, blockquote, pre, | body, | ||||||
| a, abbr, acronym, address, big, cite, code, | div, | ||||||
| del, dfn, em, img, ins, kbd, q, s, samp, | span, | ||||||
| small, strike, sub, sup, tt, var, | applet, | ||||||
| b, u, i, center, | object, | ||||||
| dl, dt, dd, ol, ul, li, | iframe, | ||||||
| fieldset, form, label, legend, | h1, | ||||||
| table, caption, tbody, tfoot, thead, tr, th, td, | h2, | ||||||
| article, aside, canvas, details, embed, | h3, | ||||||
| figure, figcaption, footer, header, hgroup, | h4, | ||||||
| menu, nav, output, ruby, section, summary, | h5, | ||||||
| time, mark, audio, video { | h6, | ||||||
|  | p, | ||||||
|  | blockquote, | ||||||
|  | pre, | ||||||
|  | a, | ||||||
|  | abbr, | ||||||
|  | acronym, | ||||||
|  | address, | ||||||
|  | big, | ||||||
|  | cite, | ||||||
|  | code, | ||||||
|  | del, | ||||||
|  | dfn, | ||||||
|  | em, | ||||||
|  | img, | ||||||
|  | ins, | ||||||
|  | kbd, | ||||||
|  | q, | ||||||
|  | s, | ||||||
|  | samp, | ||||||
|  | small, | ||||||
|  | strike, | ||||||
|  | sub, | ||||||
|  | sup, | ||||||
|  | tt, | ||||||
|  | var, | ||||||
|  | b, | ||||||
|  | u, | ||||||
|  | i, | ||||||
|  | center, | ||||||
|  | dl, | ||||||
|  | dt, | ||||||
|  | dd, | ||||||
|  | ol, | ||||||
|  | ul, | ||||||
|  | li, | ||||||
|  | fieldset, | ||||||
|  | form, | ||||||
|  | label, | ||||||
|  | legend, | ||||||
|  | table, | ||||||
|  | caption, | ||||||
|  | tbody, | ||||||
|  | tfoot, | ||||||
|  | thead, | ||||||
|  | tr, | ||||||
|  | th, | ||||||
|  | td, | ||||||
|  | article, | ||||||
|  | aside, | ||||||
|  | canvas, | ||||||
|  | details, | ||||||
|  | embed, | ||||||
|  | figure, | ||||||
|  | figcaption, | ||||||
|  | footer, | ||||||
|  | header, | ||||||
|  | hgroup, | ||||||
|  | menu, | ||||||
|  | nav, | ||||||
|  | output, | ||||||
|  | ruby, | ||||||
|  | section, | ||||||
|  | summary, | ||||||
|  | time, | ||||||
|  | mark, | ||||||
|  | audio, | ||||||
|  | video { | ||||||
| 	margin: 0; | 	margin: 0; | ||||||
| 	padding: 0; | 	padding: 0; | ||||||
| 	border: 0; | 	border: 0; | ||||||
| 	font-size: 100%; | 	font-size: 100%; | ||||||
| 	font: inherit; |  | ||||||
| 	vertical-align: baseline; | 	vertical-align: baseline; | ||||||
| } | } | ||||||
| /* HTML5 display-role reset for older browsers */ | /* HTML5 display-role reset for older browsers */ | ||||||
| article, aside, details, figcaption, figure, | article, | ||||||
| footer, header, hgroup, menu, nav, section { | aside, | ||||||
|  | details, | ||||||
|  | figcaption, | ||||||
|  | figure, | ||||||
|  | footer, | ||||||
|  | header, | ||||||
|  | hgroup, | ||||||
|  | menu, | ||||||
|  | nav, | ||||||
|  | section { | ||||||
| 	display: block; | 	display: block; | ||||||
| } | } | ||||||
| body { | body { | ||||||
| 	line-height: 1; | 	line-height: 1; | ||||||
| } | } | ||||||
| ol, ul { | ol, | ||||||
|  | ul { | ||||||
| 	/* list-style: none;*/ | 	/* list-style: none;*/ | ||||||
| } | } | ||||||
| blockquote, q { | blockquote, | ||||||
|  | q { | ||||||
| 	quotes: none; | 	quotes: none; | ||||||
| } | } | ||||||
| blockquote:before, blockquote:after, | blockquote:before, | ||||||
| q:before, q:after { | blockquote:after, | ||||||
| 	content: ''; | q:before, | ||||||
|  | q:after { | ||||||
|  | 	content: ""; | ||||||
| 	content: none; | 	content: none; | ||||||
| } | } | ||||||
| table { | table { | ||||||
|   | |||||||
| @@ -1,60 +1,66 @@ | |||||||
| /* eslint-disable camelcase */ | $(() => { | ||||||
| $(function () { |  | ||||||
| 	// const buttons = $('.choose_file_button') | 	// const buttons = $('.choose_file_button') | ||||||
|   const popups = $('.choose_file_widget') | 	const popups = $(".choose_file_widget"); | ||||||
| 	popups.dialog({ | 	popups.dialog({ | ||||||
| 		autoOpen: false, | 		autoOpen: false, | ||||||
| 		modal: true, | 		modal: true, | ||||||
|     width: '90%', | 		width: "90%", | ||||||
|     create: function (event) { | 		create: (event) => { | ||||||
|       const target = $(event.target) | 			const target = $(event.target); | ||||||
| 			target.parent().css({ | 			target.parent().css({ | ||||||
|         position: 'fixed', | 				position: "fixed", | ||||||
|         top: '5%', | 				top: "5%", | ||||||
|         bottom: '5%' | 				bottom: "5%", | ||||||
|       }) | 			}); | ||||||
|       target.css('height', '300px') | 			target.css("height", "300px"); | ||||||
|       console.log(target) | 			console.log(target); | ||||||
| 		}, | 		}, | ||||||
| 		buttons: [ | 		buttons: [ | ||||||
| 			{ | 			{ | ||||||
|         text: 'Choose', | 				text: "Choose", | ||||||
| 				click: function () { | 				click: function () { | ||||||
|           console.log($('#file_id')) | 					console.log($("#file_id")); | ||||||
|           $('input[name=' + $(this).attr('name') + ']').attr('value', $('#file_id').attr('value')) | 					$(`input[name=${$(this).attr("name")}]`).attr( | ||||||
|           $(this).dialog('close') | 						"value", | ||||||
|  | 						$("#file_id").attr("value"), | ||||||
|  | 					); | ||||||
|  | 					$(this).dialog("close"); | ||||||
| 				}, | 				}, | ||||||
|         disabled: true | 				disabled: true, | ||||||
|       } | 			}, | ||||||
|     ] | 		], | ||||||
|   }) | 	}); | ||||||
|   $('.choose_file_button').button().on('click', function () { | 	$(".choose_file_button") | ||||||
|     const popup = popups.filter('[name=' + $(this).attr('name') + ']') | 		.button() | ||||||
|     console.log(popup) | 		.on("click", function () { | ||||||
|     popup.html('<iframe src="/file/popup" width="100%" height="95%"></iframe><div id="file_id" value="null" />') | 			const popup = popups.filter(`[name=${$(this).attr("name")}]`); | ||||||
|     popup.dialog({ title: $(this).text() }).dialog('open') | 			console.log(popup); | ||||||
|   }) | 			popup.html( | ||||||
|   $('#quick_notif li').click(function () { | 				'<iframe src="/file/popup" width="100%" height="95%"></iframe><div id="file_id" value="null" />', | ||||||
|     $(this).hide() | 			); | ||||||
|   }) | 			popup.dialog({ title: $(this).text() }).dialog("open"); | ||||||
| }) | 		}); | ||||||
|  | 	$("#quick_notif li").click(function () { | ||||||
|  | 		$(this).hide(); | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|  |  | ||||||
| export function createQuickNotif (msg) { | function createQuickNotif(msg) { | ||||||
|   const el = document.createElement('li') | 	const el = document.createElement("li"); | ||||||
|   el.textContent = msg | 	el.textContent = msg; | ||||||
|   el.addEventListener('click', () => el.parentNode.removeChild(el)) | 	el.addEventListener("click", () => el.parentNode.removeChild(el)); | ||||||
|   document.getElementById('quick_notif').appendChild(el) | 	document.getElementById("quick_notif").appendChild(el); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function deleteQuickNotifs () { | function deleteQuickNotifs() { | ||||||
|   const el = document.getElementById('quick_notif') | 	const el = document.getElementById("quick_notif"); | ||||||
| 	while (el.firstChild) { | 	while (el.firstChild) { | ||||||
|     el.removeChild(el.firstChild) | 		el.removeChild(el.firstChild); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| export function display_notif () { | function display_notif() { | ||||||
|   $('#header_notif').toggle().parent().toggleClass('white') | 	$("#header_notif").toggle().parent().toggleClass("white"); | ||||||
| } | } | ||||||
|  |  | ||||||
| // You can't get the csrf token from the template in a widget | // You can't get the csrf token from the template in a widget | ||||||
| @@ -63,11 +69,11 @@ export function display_notif () { | |||||||
| // Sadly, getting the cookie is not possible with CSRF_COOKIE_HTTPONLY or CSRF_USE_SESSIONS is True | // Sadly, getting the cookie is not possible with CSRF_COOKIE_HTTPONLY or CSRF_USE_SESSIONS is True | ||||||
| // So, the true workaround is to get the token from the dom | // So, the true workaround is to get the token from the dom | ||||||
| // https://docs.djangoproject.com/en/2.0/ref/csrf/#acquiring-the-token-if-csrf-use-sessions-is-true | // https://docs.djangoproject.com/en/2.0/ref/csrf/#acquiring-the-token-if-csrf-use-sessions-is-true | ||||||
| export function getCSRFToken () { | function getCSRFToken() { | ||||||
|   return $('[name=csrfmiddlewaretoken]').val() | 	return $("[name=csrfmiddlewaretoken]").val(); | ||||||
| } | } | ||||||
|  |  | ||||||
| export const initialUrlParams = new URLSearchParams(window.location.search) | const initialUrlParams = new URLSearchParams(window.location.search); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @readonly |  * @readonly | ||||||
| @@ -76,8 +82,8 @@ export const initialUrlParams = new URLSearchParams(window.location.search) | |||||||
| const History = { | const History = { | ||||||
| 	NONE: 0, | 	NONE: 0, | ||||||
| 	PUSH: 1, | 	PUSH: 1, | ||||||
|   REPLACE: 2 | 	REPLACE: 2, | ||||||
| } | }; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @param {string} key |  * @param {string} key | ||||||
| @@ -85,27 +91,30 @@ const History = { | |||||||
|  * @param {History} action |  * @param {History} action | ||||||
|  * @param {URL | null} url |  * @param {URL | null} url | ||||||
|  */ |  */ | ||||||
| export function update_query_string (key, value, action = History.REPLACE, url = null) { | function update_query_string(key, value, action = History.REPLACE, url = null) { | ||||||
|   if (!url) { | 	let ret = url; | ||||||
|     url = new URL(window.location.href) | 	if (!ret) { | ||||||
|  | 		ret = new URL(window.location.href); | ||||||
| 	} | 	} | ||||||
|   if (value === undefined || value === null || value === '') { | 	if (value === undefined || value === null || value === "") { | ||||||
| 		// If the value is null, undefined or empty => delete it | 		// If the value is null, undefined or empty => delete it | ||||||
|     url.searchParams.delete(key) | 		ret.searchParams.delete(key); | ||||||
| 	} else if (Array.isArray(value)) { | 	} else if (Array.isArray(value)) { | ||||||
|     url.searchParams.delete(key) | 		ret.searchParams.delete(key); | ||||||
|     value.forEach((v) => url.searchParams.append(key, v)) | 		for (const v of value) { | ||||||
|  | 			ret.searchParams.append(key, v); | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
|     url.searchParams.set(key, value) | 		ret.searchParams.set(key, value); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (action === History.PUSH) { | 	if (action === History.PUSH) { | ||||||
|     window.history.pushState(null, '', url.toString()) | 		window.history.pushState(null, "", ret.toString()); | ||||||
| 	} else if (action === History.REPLACE) { | 	} else if (action === History.REPLACE) { | ||||||
|     window.history.replaceState(null, '', url.toString()) | 		window.history.replaceState(null, "", ret.toString()); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   return url | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| // TODO : If one day a test workflow is made for JS in this project | // TODO : If one day a test workflow is made for JS in this project | ||||||
| @@ -116,27 +125,29 @@ export function update_query_string (key, value, action = History.REPLACE, url = | |||||||
|  * @param {string} url The paginated endpoint to fetch |  * @param {string} url The paginated endpoint to fetch | ||||||
|  * @return {Promise<Object[]>} |  * @return {Promise<Object[]>} | ||||||
|  */ |  */ | ||||||
| export async function fetch_paginated (url) { | async function fetch_paginated(url) { | ||||||
|   const max_per_page = 199 | 	const max_per_page = 199; | ||||||
|   const paginated_url = new URL(url, document.location.origin) | 	const paginated_url = new URL(url, document.location.origin); | ||||||
|   paginated_url.searchParams.set('page_size', max_per_page.toString()) | 	paginated_url.searchParams.set("page_size", max_per_page.toString()); | ||||||
|   paginated_url.searchParams.set('page', '1') | 	paginated_url.searchParams.set("page", "1"); | ||||||
|  |  | ||||||
|   const first_page = (await (await fetch(paginated_url)).json()) | 	const first_page = await (await fetch(paginated_url)).json(); | ||||||
|   const results = first_page.results | 	const results = first_page.results; | ||||||
|  |  | ||||||
|   const nb_pictures = first_page.count | 	const nb_pictures = first_page.count; | ||||||
|   const nb_pages = Math.ceil(nb_pictures / max_per_page) | 	const nb_pages = Math.ceil(nb_pictures / max_per_page); | ||||||
|  |  | ||||||
| 	if (nb_pages > 1) { | 	if (nb_pages > 1) { | ||||||
|     const promises = [] | 		const promises = []; | ||||||
| 		for (let i = 2; i <= nb_pages; i++) { | 		for (let i = 2; i <= nb_pages; i++) { | ||||||
|       paginated_url.searchParams.set('page', i.toString()) | 			paginated_url.searchParams.set("page", i.toString()); | ||||||
| 			promises.push( | 			promises.push( | ||||||
|         fetch(paginated_url).then(res => res.json().then(json => json.results)) | 				fetch(paginated_url).then((res) => | ||||||
|       ) | 					res.json().then((json) => json.results), | ||||||
|  | 				), | ||||||
|  | 			); | ||||||
| 		} | 		} | ||||||
|     results.push(...(await Promise.all(promises)).flat()) | 		results.push(...(await Promise.all(promises)).flat()); | ||||||
| 	} | 	} | ||||||
|   return results | 	return results; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										104
									
								
								core/static/core/js/shorten.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										104
									
								
								core/static/core/js/shorten.min.js
									
									
									
									
										vendored
									
									
								
							| @@ -19,4 +19,106 @@ | |||||||
| // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||||||
| // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||||||
| // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
| !function(e){e.fn.shorten=function(s){"use strict";var t={showChars:100,minHideChars:10,ellipsesText:"...",moreText:"more",lessText:"less",onLess:function(){},onMore:function(){},errMsg:null,force:!1};return s&&e.extend(t,s),(!e(this).data("jquery.shorten")||!!t.force)&&(e(this).data("jquery.shorten",!0),e(document).off("click",".morelink"),e(document).on({click:function(){var s=e(this);return s.hasClass("less")?(s.removeClass("less"),s.html(t.moreText),s.parent().prev().animate({},function(){s.parent().prev().prev().show()}).hide("fast",function(){t.onLess()})):(s.addClass("less"),s.html(t.lessText),s.parent().prev().animate({},function(){s.parent().prev().prev().hide()}).show("fast",function(){t.onMore()})),!1}},".morelink"),this.each(function(){var s=e(this),n=s.html();if(s.text().length>t.showChars+t.minHideChars){var r=n.substr(0,t.showChars);if(r.indexOf("<")>=0){for(var a=!1,o="",i=0,l=[],h=null,c=0,f=0;f<=t.showChars;c++)if("<"!=n[c]||a||(a=!0,"/"==(h=n.substring(c+1,n.indexOf(">",c)))[0]?h!="/"+l[0]?t.errMsg="ERROR en HTML: the top of the stack should be the tag that closes":l.shift():"br"!=h.toLowerCase()&&l.unshift(h)),a&&">"==n[c]&&(a=!1),a)o+=n.charAt(c);else if(f++,i<=t.showChars)o+=n.charAt(c),i++;else if(l.length>0){for(j=0;j<l.length;j++)o+="</"+l[j]+">";break}r=e("<div/>").html(o+'<span class="ellip">'+t.ellipsesText+"</span>").html()}else r+=t.ellipsesText;var p='<div class="shortcontent">'+r+'</div><div class="allcontent">'+n+'</div><span><a href="javascript://nop/" class="morelink">'+t.moreText+"</a></span>";s.html(p),s.find(".allcontent").hide(),e(".shortcontent p:last",s).css("margin-bottom",0)}}))}}(jQuery); | !(function (e) { | ||||||
|  | 	e.fn.shorten = function (s) { | ||||||
|  | 		"use strict"; | ||||||
|  | 		var t = { | ||||||
|  | 			showChars: 100, | ||||||
|  | 			minHideChars: 10, | ||||||
|  | 			ellipsesText: "...", | ||||||
|  | 			moreText: "more", | ||||||
|  | 			lessText: "less", | ||||||
|  | 			onLess: function () {}, | ||||||
|  | 			onMore: function () {}, | ||||||
|  | 			errMsg: null, | ||||||
|  | 			force: !1, | ||||||
|  | 		}; | ||||||
|  | 		return ( | ||||||
|  | 			s && e.extend(t, s), | ||||||
|  | 			(!e(this).data("jquery.shorten") || !!t.force) && | ||||||
|  | 				(e(this).data("jquery.shorten", !0), | ||||||
|  | 				e(document).off("click", ".morelink"), | ||||||
|  | 				e(document).on( | ||||||
|  | 					{ | ||||||
|  | 						click: function () { | ||||||
|  | 							var s = e(this); | ||||||
|  | 							return ( | ||||||
|  | 								s.hasClass("less") | ||||||
|  | 									? (s.removeClass("less"), | ||||||
|  | 										s.html(t.moreText), | ||||||
|  | 										s | ||||||
|  | 											.parent() | ||||||
|  | 											.prev() | ||||||
|  | 											.animate({}, function () { | ||||||
|  | 												s.parent().prev().prev().show(); | ||||||
|  | 											}) | ||||||
|  | 											.hide("fast", function () { | ||||||
|  | 												t.onLess(); | ||||||
|  | 											})) | ||||||
|  | 									: (s.addClass("less"), | ||||||
|  | 										s.html(t.lessText), | ||||||
|  | 										s | ||||||
|  | 											.parent() | ||||||
|  | 											.prev() | ||||||
|  | 											.animate({}, function () { | ||||||
|  | 												s.parent().prev().prev().hide(); | ||||||
|  | 											}) | ||||||
|  | 											.show("fast", function () { | ||||||
|  | 												t.onMore(); | ||||||
|  | 											})), | ||||||
|  | 								!1 | ||||||
|  | 							); | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 					".morelink", | ||||||
|  | 				), | ||||||
|  | 				this.each(function () { | ||||||
|  | 					var s = e(this), | ||||||
|  | 						n = s.html(); | ||||||
|  | 					if (s.text().length > t.showChars + t.minHideChars) { | ||||||
|  | 						var r = n.substr(0, t.showChars); | ||||||
|  | 						if (r.indexOf("<") >= 0) { | ||||||
|  | 							for ( | ||||||
|  | 								var a = !1, o = "", i = 0, l = [], h = null, c = 0, f = 0; | ||||||
|  | 								f <= t.showChars; | ||||||
|  | 								c++ | ||||||
|  | 							) | ||||||
|  | 								if ( | ||||||
|  | 									("<" != n[c] || | ||||||
|  | 										a || | ||||||
|  | 										((a = !0), | ||||||
|  | 										"/" == (h = n.substring(c + 1, n.indexOf(">", c)))[0] | ||||||
|  | 											? h != "/" + l[0] | ||||||
|  | 												? (t.errMsg = | ||||||
|  | 														"ERROR en HTML: the top of the stack should be the tag that closes") | ||||||
|  | 												: l.shift() | ||||||
|  | 											: "br" != h.toLowerCase() && l.unshift(h)), | ||||||
|  | 									a && ">" == n[c] && (a = !1), | ||||||
|  | 									a) | ||||||
|  | 								) | ||||||
|  | 									o += n.charAt(c); | ||||||
|  | 								else if ((f++, i <= t.showChars)) (o += n.charAt(c)), i++; | ||||||
|  | 								else if (l.length > 0) { | ||||||
|  | 									for (j = 0; j < l.length; j++) o += "</" + l[j] + ">"; | ||||||
|  | 									break; | ||||||
|  | 								} | ||||||
|  | 							r = e("<div/>") | ||||||
|  | 								.html(o + '<span class="ellip">' + t.ellipsesText + "</span>") | ||||||
|  | 								.html(); | ||||||
|  | 						} else r += t.ellipsesText; | ||||||
|  | 						var p = | ||||||
|  | 							'<div class="shortcontent">' + | ||||||
|  | 							r + | ||||||
|  | 							'</div><div class="allcontent">' + | ||||||
|  | 							n + | ||||||
|  | 							'</div><span><a href="javascript://nop/" class="morelink">' + | ||||||
|  | 							t.moreText + | ||||||
|  | 							"</a></span>"; | ||||||
|  | 						s.html(p), | ||||||
|  | 							s.find(".allcontent").hide(), | ||||||
|  | 							e(".shortcontent p:last", s).css("margin-bottom", 0); | ||||||
|  | 					} | ||||||
|  | 				})) | ||||||
|  | 		); | ||||||
|  | 	}; | ||||||
|  | })(jQuery); | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| /* eslint-disable camelcase */ |  | ||||||
| /** | /** | ||||||
|  * Builders to use Select2 in our templates. |  * Builders to use Select2 in our templates. | ||||||
|  * |  * | ||||||
| @@ -158,15 +157,15 @@ | |||||||
| /** | /** | ||||||
|  * @param {Select2Options} options |  * @param {Select2Options} options | ||||||
|  */ |  */ | ||||||
| export function sithSelect2 (options) { | function sithSelect2(options) { | ||||||
|   const elem = $(options.element) | 	const elem = $(options.element); | ||||||
| 	return elem.select2({ | 	return elem.select2({ | ||||||
|     theme: elem[0].multiple ? 'classic' : 'default', | 		theme: elem[0].multiple ? "classic" : "default", | ||||||
| 		minimumInputLength: 2, | 		minimumInputLength: 2, | ||||||
| 		templateResult: select_item_builder(options.picture_getter), | 		templateResult: select_item_builder(options.picture_getter), | ||||||
| 		...options.data_source, | 		...options.data_source, | ||||||
|     ...(options.overrides || {}) | 		...(options.overrides || {}), | ||||||
|   }) | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -180,12 +179,12 @@ export function sithSelect2 (options) { | |||||||
|  * @param {Select2Object[]} source The array containing the data |  * @param {Select2Object[]} source The array containing the data | ||||||
|  * @param {RemoteSourceOptions} options |  * @param {RemoteSourceOptions} options | ||||||
|  */ |  */ | ||||||
| export function local_data_source (source, options) { | function local_data_source(source, options) { | ||||||
| 	if (options.excluded) { | 	if (options.excluded) { | ||||||
|     const ids = options.excluded() | 		const ids = options.excluded(); | ||||||
|     return { data: source.filter((i) => !ids.includes(i.id)) } | 		return { data: source.filter((i) => !ids.includes(i.id)) }; | ||||||
| 	} | 	} | ||||||
|   return { data: source } | 	return { data: source }; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -203,37 +202,37 @@ export function local_data_source (source, options) { | |||||||
|  * @param {string} source The url of the endpoint |  * @param {string} source The url of the endpoint | ||||||
|  * @param {RemoteSourceOptions} options |  * @param {RemoteSourceOptions} options | ||||||
|  */ |  */ | ||||||
| export function remote_data_source (source, options) { | function remote_data_source(source, options) { | ||||||
|   jQuery.ajaxSettings.traditional = true | 	jQuery.ajaxSettings.traditional = true; | ||||||
| 	const params = { | 	const params = { | ||||||
| 		url: source, | 		url: source, | ||||||
|     dataType: 'json', | 		dataType: "json", | ||||||
| 		cache: true, | 		cache: true, | ||||||
| 		delay: 250, | 		delay: 250, | ||||||
| 		data: function (params) { | 		data: function (params) { | ||||||
| 			return { | 			return { | ||||||
| 				search: params.term, | 				search: params.term, | ||||||
| 				exclude: [ | 				exclude: [ | ||||||
|           ...(this.val() || []).map((i) => parseInt(i)), | 					...(this.val() || []).map((i) => Number.parseInt(i)), | ||||||
|           ...(options.excluded ? options.excluded() : []) | 					...(options.excluded ? options.excluded() : []), | ||||||
|         ] | 				], | ||||||
|       } | 			}; | ||||||
|     } | 		}, | ||||||
|   } | 	}; | ||||||
| 	if (options.result_converter) { | 	if (options.result_converter) { | ||||||
|     params.processResults = function (data) { | 		params.processResults = (data) => ({ | ||||||
|       return { results: data.results.map(options.result_converter) } | 			results: data.results.map(options.result_converter), | ||||||
|     } | 		}); | ||||||
| 	} | 	} | ||||||
| 	if (options.overrides) { | 	if (options.overrides) { | ||||||
|     Object.assign(params, options.overrides) | 		Object.assign(params, options.overrides); | ||||||
| 	} | 	} | ||||||
|   return { ajax: params } | 	return { ajax: params }; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function item_formatter (user) { | function item_formatter(user) { | ||||||
| 	if (user.loading) { | 	if (user.loading) { | ||||||
|     return user.text | 		return user.text; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -242,21 +241,21 @@ export function item_formatter (user) { | |||||||
|  * @param {null | function(Object):string} picture_getter |  * @param {null | function(Object):string} picture_getter | ||||||
|  * @return {function(string): jQuery|HTMLElement} |  * @return {function(string): jQuery|HTMLElement} | ||||||
|  */ |  */ | ||||||
| function select_item_builder (picture_getter) { | function select_item_builder(picture_getter) { | ||||||
| 	return (item) => { | 	return (item) => { | ||||||
| 		const picture = | 		const picture = | ||||||
|       typeof picture_getter === 'function' ? picture_getter(item) : null | 			typeof picture_getter === "function" ? picture_getter(item) : null; | ||||||
| 		const img_html = picture | 		const img_html = picture | ||||||
| 			? `<img  | 			? `<img  | ||||||
|           src="${picture_getter(item)}"  |           src="${picture_getter(item)}"  | ||||||
|           alt="${item.text}"  |           alt="${item.text}"  | ||||||
|           onerror="this.src = '/static/core/img/unknown.jpg'"  |           onerror="this.src = '/static/core/img/unknown.jpg'"  | ||||||
|         />` |         />` | ||||||
|       : '' | 			: ""; | ||||||
|  |  | ||||||
| 		return $(`<div class="select-item"> | 		return $(`<div class="select-item"> | ||||||
|         ${img_html} |         ${img_html} | ||||||
|          <span class="select-item-text">${item.text}</span> |          <span class="select-item-text">${item.text}</span> | ||||||
|          </div>`) |          </div>`); | ||||||
|   } | 	}; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,25 +1,22 @@ | |||||||
| /* eslint-disable camelcase */ | async function get_graph_data(url, godfathers_depth, godchildren_depth) { | ||||||
| /* global cytoscape, initialUrlParams, History, update_query_string */ |  | ||||||
|  |  | ||||||
| async function get_graph_data (url, godfathers_depth, godchildren_depth) { |  | ||||||
| 	const data = await ( | 	const data = await ( | ||||||
| 		await fetch( | 		await fetch( | ||||||
|       `${url}?godfathers_depth=${godfathers_depth}&godchildren_depth=${godchildren_depth}` | 			`${url}?godfathers_depth=${godfathers_depth}&godchildren_depth=${godchildren_depth}`, | ||||||
| 		) | 		) | ||||||
|   ).json() | 	).json(); | ||||||
| 	return [ | 	return [ | ||||||
| 		...data.users.map((user) => { | 		...data.users.map((user) => { | ||||||
|       return { data: user } | 			return { data: user }; | ||||||
| 		}), | 		}), | ||||||
| 		...data.relationships.map((rel) => { | 		...data.relationships.map((rel) => { | ||||||
| 			return { | 			return { | ||||||
|         data: { source: rel.godfather, target: rel.godchild } | 				data: { source: rel.godfather, target: rel.godchild }, | ||||||
|       } | 			}; | ||||||
|     }) | 		}), | ||||||
|   ] | 	]; | ||||||
| } | } | ||||||
|  |  | ||||||
| function create_graph (container, data, active_user_id) { | function create_graph(container, data, active_user_id) { | ||||||
| 	const cy = cytoscape({ | 	const cy = cytoscape({ | ||||||
| 		boxSelectionEnabled: false, | 		boxSelectionEnabled: false, | ||||||
| 		autounselectify: true, | 		autounselectify: true, | ||||||
| @@ -31,141 +28,139 @@ function create_graph (container, data, active_user_id) { | |||||||
| 		style: [ | 		style: [ | ||||||
| 			// the stylesheet for the graph | 			// the stylesheet for the graph | ||||||
| 			{ | 			{ | ||||||
|         selector: 'node', | 				selector: "node", | ||||||
| 				style: { | 				style: { | ||||||
|           label: 'data(display_name)', | 					label: "data(display_name)", | ||||||
|           'background-image': 'data(profile_pict)', | 					"background-image": "data(profile_pict)", | ||||||
|           width: '100%', | 					width: "100%", | ||||||
|           height: '100%', | 					height: "100%", | ||||||
|           'background-fit': 'cover', | 					"background-fit": "cover", | ||||||
|           'background-repeat': 'no-repeat', | 					"background-repeat": "no-repeat", | ||||||
|           shape: 'ellipse' | 					shape: "ellipse", | ||||||
|         } | 				}, | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
| 			{ | 			{ | ||||||
|         selector: 'edge', | 				selector: "edge", | ||||||
| 				style: { | 				style: { | ||||||
| 					width: 5, | 					width: 5, | ||||||
|           'line-color': '#ccc', | 					"line-color": "#ccc", | ||||||
|           'target-arrow-color': '#ccc', | 					"target-arrow-color": "#ccc", | ||||||
|           'target-arrow-shape': 'triangle', | 					"target-arrow-shape": "triangle", | ||||||
|           'curve-style': 'bezier' | 					"curve-style": "bezier", | ||||||
|         } | 				}, | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
| 			{ | 			{ | ||||||
|         selector: '.traversed', | 				selector: ".traversed", | ||||||
| 				style: { | 				style: { | ||||||
|           'border-width': '5px', | 					"border-width": "5px", | ||||||
|           'border-style': 'solid', | 					"border-style": "solid", | ||||||
|           'border-color': 'red', | 					"border-color": "red", | ||||||
|           'target-arrow-color': 'red', | 					"target-arrow-color": "red", | ||||||
|           'line-color': 'red' | 					"line-color": "red", | ||||||
|         } | 				}, | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
| 			{ | 			{ | ||||||
|         selector: '.not-traversed', | 				selector: ".not-traversed", | ||||||
| 				style: { | 				style: { | ||||||
|           'line-opacity': '0.5', | 					"line-opacity": "0.5", | ||||||
|           'background-opacity': '0.5', | 					"background-opacity": "0.5", | ||||||
|           'background-image-opacity': '0.5' | 					"background-image-opacity": "0.5", | ||||||
|         } | 				}, | ||||||
|       } | 			}, | ||||||
| 		], | 		], | ||||||
| 		layout: { | 		layout: { | ||||||
|       name: 'klay', | 			name: "klay", | ||||||
| 			nodeDimensionsIncludeLabels: true, | 			nodeDimensionsIncludeLabels: true, | ||||||
| 			fit: true, | 			fit: true, | ||||||
| 			klay: { | 			klay: { | ||||||
| 				addUnnecessaryBendpoints: true, | 				addUnnecessaryBendpoints: true, | ||||||
|         direction: 'DOWN', | 				direction: "DOWN", | ||||||
|         nodePlacement: 'INTERACTIVE', | 				nodePlacement: "INTERACTIVE", | ||||||
|         layoutHierarchy: true | 				layoutHierarchy: true, | ||||||
|       } | 			}, | ||||||
|     } | 		}, | ||||||
|   }) | 	}); | ||||||
| 	const active_user = cy | 	const active_user = cy | ||||||
| 		.getElementById(active_user_id) | 		.getElementById(active_user_id) | ||||||
|     .style('shape', 'rectangle') | 		.style("shape", "rectangle"); | ||||||
| 	/* Reset graph */ | 	/* Reset graph */ | ||||||
| 	const reset_graph = () => { | 	const reset_graph = () => { | ||||||
| 		cy.elements((element) => { | 		cy.elements((element) => { | ||||||
|       if (element.hasClass('traversed')) { | 			if (element.hasClass("traversed")) { | ||||||
|         element.removeClass('traversed') | 				element.removeClass("traversed"); | ||||||
| 			} | 			} | ||||||
|       if (element.hasClass('not-traversed')) { | 			if (element.hasClass("not-traversed")) { | ||||||
|         element.removeClass('not-traversed') | 				element.removeClass("not-traversed"); | ||||||
|       } |  | ||||||
|     }) |  | ||||||
| 			} | 			} | ||||||
|  | 		}); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
| 	const on_node_tap = (el) => { | 	const on_node_tap = (el) => { | ||||||
|     reset_graph() | 		reset_graph(); | ||||||
| 		/* Create path on graph if selected isn't the targeted user */ | 		/* Create path on graph if selected isn't the targeted user */ | ||||||
| 		if (el === active_user) { | 		if (el === active_user) { | ||||||
|       return | 			return; | ||||||
| 		} | 		} | ||||||
| 		cy.elements((element) => { | 		cy.elements((element) => { | ||||||
|       element.addClass('not-traversed') | 			element.addClass("not-traversed"); | ||||||
|     }) | 		}); | ||||||
|  |  | ||||||
|     cy.elements() | 		for (const traversed of cy.elements().aStar({ | ||||||
|       .aStar({ |  | ||||||
| 			root: el, | 			root: el, | ||||||
|         goal: active_user | 			goal: active_user, | ||||||
|       }) | 		}).path) { | ||||||
|       .path.forEach((el) => { | 			traversed.removeClass("not-traversed"); | ||||||
|         el.removeClass('not-traversed') | 			traversed.addClass("traversed"); | ||||||
|         el.addClass('traversed') |  | ||||||
|       }) |  | ||||||
| 		} | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|   cy.on('tap', 'node', (tapped) => { | 	cy.on("tap", "node", (tapped) => { | ||||||
|     on_node_tap(tapped.target) | 		on_node_tap(tapped.target); | ||||||
|   }) | 	}); | ||||||
|   cy.zoomingEnabled(false) | 	cy.zoomingEnabled(false); | ||||||
|  |  | ||||||
| 	/* Add context menu */ | 	/* Add context menu */ | ||||||
| 	if (cy.cxtmenu === undefined) { | 	if (cy.cxtmenu === undefined) { | ||||||
| 		console.error( | 		console.error( | ||||||
|       "ctxmenu isn't loaded, context menu won't be available on graphs" | 			"ctxmenu isn't loaded, context menu won't be available on graphs", | ||||||
|     ) | 		); | ||||||
|     return cy | 		return cy; | ||||||
| 	} | 	} | ||||||
| 	cy.cxtmenu({ | 	cy.cxtmenu({ | ||||||
|     selector: 'node', | 		selector: "node", | ||||||
|  |  | ||||||
| 		commands: [ | 		commands: [ | ||||||
| 			{ | 			{ | ||||||
| 				content: '<i class="fa fa-external-link fa-2x"></i>', | 				content: '<i class="fa fa-external-link fa-2x"></i>', | ||||||
|         select: function (el) { | 				select: (el) => { | ||||||
|           window.open(el.data().profile_url, '_blank').focus() | 					window.open(el.data().profile_url, "_blank").focus(); | ||||||
|         } | 				}, | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
| 			{ | 			{ | ||||||
| 				content: '<span class="fa fa-mouse-pointer fa-2x"></span>', | 				content: '<span class="fa fa-mouse-pointer fa-2x"></span>', | ||||||
|         select: function (el) { | 				select: (el) => { | ||||||
|           on_node_tap(el) | 					on_node_tap(el); | ||||||
|         } | 				}, | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
| 			{ | 			{ | ||||||
| 				content: '<i class="fa fa-eraser fa-2x"></i>', | 				content: '<i class="fa fa-eraser fa-2x"></i>', | ||||||
|         select: function (el) { | 				select: (el) => { | ||||||
|           reset_graph() | 					reset_graph(); | ||||||
|         } | 				}, | ||||||
|       } | 			}, | ||||||
|     ] | 		], | ||||||
|   }) | 	}); | ||||||
|  |  | ||||||
|   return cy | 	return cy; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* global api_url, active_user, depth_min, depth_max */ | /* global api_url, active_user, depth_min, depth_max */ | ||||||
| document.addEventListener('alpine:init', () => { | document.addEventListener("alpine:init", () => { | ||||||
| 	/* | 	/* | ||||||
|     This needs some constants to be set before the document has been loaded |     This needs some constants to be set before the document has been loaded | ||||||
|  |  | ||||||
| @@ -174,104 +169,104 @@ document.addEventListener('alpine:init', () => { | |||||||
|     depth_min:   minimum tree depth for godfathers and godchildren as an int |     depth_min:   minimum tree depth for godfathers and godchildren as an int | ||||||
|     depth_max:   maximum tree depth for godfathers and godchildren as an int |     depth_max:   maximum tree depth for godfathers and godchildren as an int | ||||||
|   */ |   */ | ||||||
|   const default_depth = 2 | 	const default_depth = 2; | ||||||
|  |  | ||||||
| 	if ( | 	if ( | ||||||
|     typeof api_url === 'undefined' || | 		typeof api_url === "undefined" || | ||||||
|     typeof active_user === 'undefined' || | 		typeof active_user === "undefined" || | ||||||
|     typeof depth_min === 'undefined' || | 		typeof depth_min === "undefined" || | ||||||
|     typeof depth_max === 'undefined' | 		typeof depth_max === "undefined" | ||||||
| 	) { | 	) { | ||||||
| 		console.error( | 		console.error( | ||||||
|       'Some constants are not set before using the family_graph script, please look at the documentation' | 			"Some constants are not set before using the family_graph script, please look at the documentation", | ||||||
|     ) | 		); | ||||||
|     return | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   function get_initial_depth (prop) { | 	function get_initial_depth(prop) { | ||||||
|     const value = parseInt(initialUrlParams.get(prop)) | 		const value = Number.parseInt(initialUrlParams.get(prop)); | ||||||
|     if (isNaN(value) || value < depth_min || value > depth_max) { | 		if (Number.isNaN(value) || value < depth_min || value > depth_max) { | ||||||
|       return default_depth | 			return default_depth; | ||||||
| 		} | 		} | ||||||
|     return value | 		return value; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   Alpine.data('graph', () => ({ | 	Alpine.data("graph", () => ({ | ||||||
| 		loading: false, | 		loading: false, | ||||||
|     godfathers_depth: get_initial_depth('godfathers_depth'), | 		godfathers_depth: get_initial_depth("godfathers_depth"), | ||||||
|     godchildren_depth: get_initial_depth('godchildren_depth'), | 		godchildren_depth: get_initial_depth("godchildren_depth"), | ||||||
|     reverse: initialUrlParams.get('reverse')?.toLowerCase?.() === 'true', | 		reverse: initialUrlParams.get("reverse")?.toLowerCase?.() === "true", | ||||||
| 		graph: undefined, | 		graph: undefined, | ||||||
| 		graph_data: {}, | 		graph_data: {}, | ||||||
|  |  | ||||||
|     async init () { | 		async init() { | ||||||
| 			const delayed_fetch = Alpine.debounce(async () => { | 			const delayed_fetch = Alpine.debounce(async () => { | ||||||
|         this.fetch_graph_data() | 				this.fetch_graph_data(); | ||||||
| 			}, 100); | 			}, 100); | ||||||
|       ['godfathers_depth', 'godchildren_depth'].forEach((param) => { | 			for (const param of ["godfathers_depth", "godchildren_depth"]) { | ||||||
| 				this.$watch(param, async (value) => { | 				this.$watch(param, async (value) => { | ||||||
| 					if (value < depth_min || value > depth_max) { | 					if (value < depth_min || value > depth_max) { | ||||||
|             return | 						return; | ||||||
| 					} | 					} | ||||||
|           update_query_string(param, value, History.REPLACE) | 					update_query_string(param, value, History.REPLACE); | ||||||
|           delayed_fetch() | 					delayed_fetch(); | ||||||
|         }) | 				}); | ||||||
|       }) | 			} | ||||||
|       this.$watch('reverse', async (value) => { | 			this.$watch("reverse", async (value) => { | ||||||
|         update_query_string('reverse', value, History.REPLACE) | 				update_query_string("reverse", value, History.REPLACE); | ||||||
|         this.reverse_graph() | 				this.reverse_graph(); | ||||||
|       }) | 			}); | ||||||
|       this.$watch('graph_data', async () => { | 			this.$watch("graph_data", async () => { | ||||||
|         await this.generate_graph() | 				await this.generate_graph(); | ||||||
| 				if (this.reverse) { | 				if (this.reverse) { | ||||||
|           await this.reverse_graph() | 					await this.reverse_graph(); | ||||||
| 				} | 				} | ||||||
|       }) | 			}); | ||||||
|       this.fetch_graph_data() | 			this.fetch_graph_data(); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async screenshot () { | 		async screenshot() { | ||||||
|       const link = document.createElement('a') | 			const link = document.createElement("a"); | ||||||
|       link.href = this.graph.jpg() | 			link.href = this.graph.jpg(); | ||||||
| 			link.download = interpolate( | 			link.download = interpolate( | ||||||
|         gettext('family_tree.%(extension)s'), | 				gettext("family_tree.%(extension)s"), | ||||||
|         { extension: 'jpg' }, | 				{ extension: "jpg" }, | ||||||
|         true | 				true, | ||||||
|       ) | 			); | ||||||
|       document.body.appendChild(link) | 			document.body.appendChild(link); | ||||||
|       link.click() | 			link.click(); | ||||||
|       document.body.removeChild(link) | 			document.body.removeChild(link); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async reset () { | 		async reset() { | ||||||
|       this.reverse = false | 			this.reverse = false; | ||||||
|       this.godfathers_depth = default_depth | 			this.godfathers_depth = default_depth; | ||||||
|       this.godchildren_depth = default_depth | 			this.godchildren_depth = default_depth; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async reverse_graph () { | 		async reverse_graph() { | ||||||
| 			this.graph.elements((el) => { | 			this.graph.elements((el) => { | ||||||
|         el.position({ x: -el.position().x, y: -el.position().y }) | 				el.position({ x: -el.position().x, y: -el.position().y }); | ||||||
|       }) | 			}); | ||||||
|       this.graph.center(this.graph.elements()) | 			this.graph.center(this.graph.elements()); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async fetch_graph_data () { | 		async fetch_graph_data() { | ||||||
| 			this.graph_data = await get_graph_data( | 			this.graph_data = await get_graph_data( | ||||||
| 				api_url, | 				api_url, | ||||||
| 				this.godfathers_depth, | 				this.godfathers_depth, | ||||||
|         this.godchildren_depth | 				this.godchildren_depth, | ||||||
|       ) | 			); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async generate_graph () { | 		async generate_graph() { | ||||||
|       this.loading = true | 			this.loading = true; | ||||||
| 			this.graph = create_graph( | 			this.graph = create_graph( | ||||||
| 				$(this.$refs.graph), | 				$(this.$refs.graph), | ||||||
| 				this.graph_data, | 				this.graph_data, | ||||||
|         active_user | 				active_user, | ||||||
|       ) | 			); | ||||||
|       this.loading = false | 			this.loading = false; | ||||||
|     } | 		}, | ||||||
|   })) | 	})); | ||||||
| }) | }); | ||||||
|   | |||||||
| @@ -1,9 +1,7 @@ | |||||||
| /* eslint-disable camelcase */ | function alpine_webcam_builder( | ||||||
| /* global DataTransfer */ |  | ||||||
| export function alpine_webcam_builder ( |  | ||||||
| 	default_picture, | 	default_picture, | ||||||
| 	delete_url, | 	delete_url, | ||||||
|   can_delete_picture | 	can_delete_picture, | ||||||
| ) { | ) { | ||||||
| 	return () => ({ | 	return () => ({ | ||||||
| 		can_edit_picture: false, | 		can_edit_picture: false, | ||||||
| @@ -15,99 +13,99 @@ export function alpine_webcam_builder ( | |||||||
| 		video: null, | 		video: null, | ||||||
| 		picture_form: null, | 		picture_form: null, | ||||||
|  |  | ||||||
|     init () { | 		init() { | ||||||
|       this.video = this.$refs.video | 			this.video = this.$refs.video; | ||||||
|       this.picture_form = this.$refs.form.getElementsByTagName('input') | 			this.picture_form = this.$refs.form.getElementsByTagName("input"); | ||||||
| 			if (this.picture_form.length > 0) { | 			if (this.picture_form.length > 0) { | ||||||
|         this.picture_form = this.picture_form[0] | 				this.picture_form = this.picture_form[0]; | ||||||
|         this.can_edit_picture = true | 				this.can_edit_picture = true; | ||||||
|  |  | ||||||
| 				// Link the displayed element to the form input | 				// Link the displayed element to the form input | ||||||
| 				this.picture_form.onchange = (event) => { | 				this.picture_form.onchange = (event) => { | ||||||
|           const files = event.srcElement.files | 					const files = event.srcElement.files; | ||||||
| 					if (files.length > 0) { | 					if (files.length > 0) { | ||||||
| 						this.picture = (window.URL || window.webkitURL).createObjectURL( | 						this.picture = (window.URL || window.webkitURL).createObjectURL( | ||||||
|               event.srcElement.files[0] | 							event.srcElement.files[0], | ||||||
|             ) | 						); | ||||||
| 					} else { | 					} else { | ||||||
|             this.picture = null | 						this.picture = null; | ||||||
|           } |  | ||||||
| 					} | 					} | ||||||
|  | 				}; | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     get_picture () { | 		get_picture() { | ||||||
|       return this.picture || default_picture | 			return this.picture || default_picture; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     delete_picture () { | 		delete_picture() { | ||||||
| 			// Only remove currently displayed picture | 			// Only remove currently displayed picture | ||||||
| 			if (this.picture) { | 			if (this.picture) { | ||||||
|         const list = new DataTransfer() | 				const list = new DataTransfer(); | ||||||
|         this.picture_form.files = list.files | 				this.picture_form.files = list.files; | ||||||
|         this.picture_form.dispatchEvent(new Event('change')) | 				this.picture_form.dispatchEvent(new Event("change")); | ||||||
|         return | 				return; | ||||||
| 			} | 			} | ||||||
| 			if (!can_delete_picture) { | 			if (!can_delete_picture) { | ||||||
|         return | 				return; | ||||||
| 			} | 			} | ||||||
| 			// Remove user picture if correct rights are available | 			// Remove user picture if correct rights are available | ||||||
|       window.open(delete_url, '_self') | 			window.open(delete_url, "_self"); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     enable_camera () { | 		enable_camera() { | ||||||
|       this.picture = null | 			this.picture = null; | ||||||
|       this.loading = true | 			this.loading = true; | ||||||
|       this.is_camera_error = false | 			this.is_camera_error = false; | ||||||
| 			navigator.mediaDevices | 			navigator.mediaDevices | ||||||
| 				.getUserMedia({ video: true, audio: false }) | 				.getUserMedia({ video: true, audio: false }) | ||||||
| 				.then((stream) => { | 				.then((stream) => { | ||||||
|           this.loading = false | 					this.loading = false; | ||||||
|           this.is_camera_enabled = true | 					this.is_camera_enabled = true; | ||||||
|           this.video.srcObject = stream | 					this.video.srcObject = stream; | ||||||
|           this.video.play() | 					this.video.play(); | ||||||
| 				}) | 				}) | ||||||
| 				.catch((err) => { | 				.catch((err) => { | ||||||
|           this.is_camera_error = true | 					this.is_camera_error = true; | ||||||
|           this.loading = false | 					this.loading = false; | ||||||
|           throw (err) | 					throw err; | ||||||
|         }) | 				}); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     take_picture () { | 		take_picture() { | ||||||
|       const canvas = document.createElement('canvas') | 			const canvas = document.createElement("canvas"); | ||||||
|       const context = canvas.getContext('2d') | 			const context = canvas.getContext("2d"); | ||||||
|  |  | ||||||
| 			/* Create the image */ | 			/* Create the image */ | ||||||
|       const settings = this.video.srcObject.getTracks()[0].getSettings() | 			const settings = this.video.srcObject.getTracks()[0].getSettings(); | ||||||
|       canvas.width = settings.width | 			canvas.width = settings.width; | ||||||
|       canvas.height = settings.height | 			canvas.height = settings.height; | ||||||
|       context.drawImage(this.video, 0, 0, canvas.width, canvas.height) | 			context.drawImage(this.video, 0, 0, canvas.width, canvas.height); | ||||||
|  |  | ||||||
| 			/* Stop camera */ | 			/* Stop camera */ | ||||||
|       this.video.pause() | 			this.video.pause(); | ||||||
|       this.video.srcObject.getTracks().forEach((track) => { | 			for (const track of this.video.srcObject.getTracks()) { | ||||||
|         if (track.readyState === 'live') { | 				if (track.readyState === "live") { | ||||||
|           track.stop() | 					track.stop(); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
|       }) |  | ||||||
|  |  | ||||||
| 			canvas.toBlob((blob) => { | 			canvas.toBlob((blob) => { | ||||||
|         const filename = interpolate(gettext('captured.%s'), ['webp']) | 				const filename = interpolate(gettext("captured.%s"), ["webp"]); | ||||||
| 				const file = new File([blob], filename, { | 				const file = new File([blob], filename, { | ||||||
|           type: 'image/webp' | 					type: "image/webp", | ||||||
|         }) | 				}); | ||||||
|  |  | ||||||
|         const list = new DataTransfer() | 				const list = new DataTransfer(); | ||||||
|         list.items.add(file) | 				list.items.add(file); | ||||||
|         this.picture_form.files = list.files | 				this.picture_form.files = list.files; | ||||||
|  |  | ||||||
| 				// No change event is triggered, we trigger it manually #} | 				// No change event is triggered, we trigger it manually #} | ||||||
|         this.picture_form.dispatchEvent(new Event('change')) | 				this.picture_form.dispatchEvent(new Event("change")); | ||||||
|       }, 'image/webp') | 			}, "image/webp"); | ||||||
|  |  | ||||||
|       canvas.remove() | 			canvas.remove(); | ||||||
|       this.is_camera_enabled = false | 			this.is_camera_enabled = false; | ||||||
|     } | 		}, | ||||||
|   }) | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import Alpine from 'alpinejs' | import Alpine from "alpinejs"; | ||||||
|  |  | ||||||
| window.Alpine = Alpine | window.Alpine = Alpine; | ||||||
|  |  | ||||||
| window.addEventListener('DOMContentLoaded', (event) => { | window.addEventListener("DOMContentLoaded", (event) => { | ||||||
|   Alpine.start() | 	Alpine.start(); | ||||||
| }) | }); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import 'codemirror/lib/codemirror.css' | import "codemirror/lib/codemirror.css"; | ||||||
| import 'easymde/src/css/easymde.css' | import "easymde/src/css/easymde.css"; | ||||||
| import EasyMDE from 'easymde' | import EasyMDE from "easymde"; | ||||||
|  |  | ||||||
| // This scripts dependens on Alpine but it should be loaded on every page | // This scripts dependens on Alpine but it should be loaded on every page | ||||||
|  |  | ||||||
| @@ -9,181 +9,181 @@ import EasyMDE from 'easymde' | |||||||
|  * @param {HTMLTextAreaElement} textarea to use |  * @param {HTMLTextAreaElement} textarea to use | ||||||
|  * @param {string} link to the markdown api |  * @param {string} link to the markdown api | ||||||
|  **/ |  **/ | ||||||
| export function easymdeFactory (textarea, markdownApiURL) { | function easymdeFactory(textarea, markdownApiURL) { | ||||||
| 	const easymde = new EasyMDE({ | 	const easymde = new EasyMDE({ | ||||||
| 		element: textarea, | 		element: textarea, | ||||||
| 		spellChecker: false, | 		spellChecker: false, | ||||||
| 		autoDownloadFontAwesome: false, | 		autoDownloadFontAwesome: false, | ||||||
| 		previewRender: Alpine.debounce(async (plainText, preview) => { | 		previewRender: Alpine.debounce(async (plainText, preview) => { | ||||||
| 			const res = await fetch(markdownApiURL, { | 			const res = await fetch(markdownApiURL, { | ||||||
|         method: 'POST', | 				method: "POST", | ||||||
|         body: JSON.stringify({ text: plainText }) | 				body: JSON.stringify({ text: plainText }), | ||||||
|       }) | 			}); | ||||||
|       preview.innerHTML = await res.text() | 			preview.innerHTML = await res.text(); | ||||||
|       return null | 			return null; | ||||||
| 		}, 300), | 		}, 300), | ||||||
| 		forceSync: true, // Avoid validation error on generic create view | 		forceSync: true, // Avoid validation error on generic create view | ||||||
| 		toolbar: [ | 		toolbar: [ | ||||||
| 			{ | 			{ | ||||||
|         name: 'heading-smaller', | 				name: "heading-smaller", | ||||||
| 				action: EasyMDE.toggleHeadingSmaller, | 				action: EasyMDE.toggleHeadingSmaller, | ||||||
|         className: 'fa fa-header', | 				className: "fa fa-header", | ||||||
|         title: gettext('Heading') | 				title: gettext("Heading"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'italic', | 				name: "italic", | ||||||
| 				action: EasyMDE.toggleItalic, | 				action: EasyMDE.toggleItalic, | ||||||
|         className: 'fa fa-italic', | 				className: "fa fa-italic", | ||||||
|         title: gettext('Italic') | 				title: gettext("Italic"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'bold', | 				name: "bold", | ||||||
| 				action: EasyMDE.toggleBold, | 				action: EasyMDE.toggleBold, | ||||||
|         className: 'fa fa-bold', | 				className: "fa fa-bold", | ||||||
|         title: gettext('Bold') | 				title: gettext("Bold"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'strikethrough', | 				name: "strikethrough", | ||||||
| 				action: EasyMDE.toggleStrikethrough, | 				action: EasyMDE.toggleStrikethrough, | ||||||
|         className: 'fa fa-strikethrough', | 				className: "fa fa-strikethrough", | ||||||
|         title: gettext('Strikethrough') | 				title: gettext("Strikethrough"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'underline', | 				name: "underline", | ||||||
|         action: function customFunction (editor) { | 				action: function customFunction(editor) { | ||||||
|           const cm = editor.codemirror | 					const cm = editor.codemirror; | ||||||
|           cm.replaceSelection('__' + cm.getSelection() + '__') | 					cm.replaceSelection(`__${cm.getSelection()}__`); | ||||||
| 				}, | 				}, | ||||||
|         className: 'fa fa-underline', | 				className: "fa fa-underline", | ||||||
|         title: gettext('Underline') | 				title: gettext("Underline"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'superscript', | 				name: "superscript", | ||||||
|         action: function customFunction (editor) { | 				action: function customFunction(editor) { | ||||||
|           const cm = editor.codemirror | 					const cm = editor.codemirror; | ||||||
|           cm.replaceSelection('^' + cm.getSelection() + '^') | 					cm.replaceSelection(`^${cm.getSelection()}^`); | ||||||
| 				}, | 				}, | ||||||
|         className: 'fa fa-superscript', | 				className: "fa fa-superscript", | ||||||
|         title: gettext('Superscript') | 				title: gettext("Superscript"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'subscript', | 				name: "subscript", | ||||||
|         action: function customFunction (editor) { | 				action: function customFunction(editor) { | ||||||
|           const cm = editor.codemirror | 					const cm = editor.codemirror; | ||||||
|           cm.replaceSelection('~' + cm.getSelection() + '~') | 					cm.replaceSelection(`~${cm.getSelection()}~`); | ||||||
| 				}, | 				}, | ||||||
|         className: 'fa fa-subscript', | 				className: "fa fa-subscript", | ||||||
|         title: gettext('Subscript') | 				title: gettext("Subscript"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'code', | 				name: "code", | ||||||
| 				action: EasyMDE.toggleCodeBlock, | 				action: EasyMDE.toggleCodeBlock, | ||||||
|         className: 'fa fa-code', | 				className: "fa fa-code", | ||||||
|         title: gettext('Code') | 				title: gettext("Code"), | ||||||
| 			}, | 			}, | ||||||
|       '|', | 			"|", | ||||||
| 			{ | 			{ | ||||||
|         name: 'quote', | 				name: "quote", | ||||||
| 				action: EasyMDE.toggleBlockquote, | 				action: EasyMDE.toggleBlockquote, | ||||||
|         className: 'fa fa-quote-left', | 				className: "fa fa-quote-left", | ||||||
|         title: gettext('Quote') | 				title: gettext("Quote"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'unordered-list', | 				name: "unordered-list", | ||||||
| 				action: EasyMDE.toggleUnorderedList, | 				action: EasyMDE.toggleUnorderedList, | ||||||
|         className: 'fa fa-list-ul', | 				className: "fa fa-list-ul", | ||||||
|         title: gettext('Unordered list') | 				title: gettext("Unordered list"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'ordered-list', | 				name: "ordered-list", | ||||||
| 				action: EasyMDE.toggleOrderedList, | 				action: EasyMDE.toggleOrderedList, | ||||||
|         className: 'fa fa-list-ol', | 				className: "fa fa-list-ol", | ||||||
|         title: gettext('Ordered list') | 				title: gettext("Ordered list"), | ||||||
| 			}, | 			}, | ||||||
|       '|', | 			"|", | ||||||
| 			{ | 			{ | ||||||
|         name: 'link', | 				name: "link", | ||||||
| 				action: EasyMDE.drawLink, | 				action: EasyMDE.drawLink, | ||||||
|         className: 'fa fa-link', | 				className: "fa fa-link", | ||||||
|         title: gettext('Insert link') | 				title: gettext("Insert link"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'image', | 				name: "image", | ||||||
| 				action: EasyMDE.drawImage, | 				action: EasyMDE.drawImage, | ||||||
|         className: 'fa-regular fa-image', | 				className: "fa-regular fa-image", | ||||||
|         title: gettext('Insert image') | 				title: gettext("Insert image"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'table', | 				name: "table", | ||||||
| 				action: EasyMDE.drawTable, | 				action: EasyMDE.drawTable, | ||||||
|         className: 'fa fa-table', | 				className: "fa fa-table", | ||||||
|         title: gettext('Insert table') | 				title: gettext("Insert table"), | ||||||
| 			}, | 			}, | ||||||
|       '|', | 			"|", | ||||||
| 			{ | 			{ | ||||||
|         name: 'clean-block', | 				name: "clean-block", | ||||||
| 				action: EasyMDE.cleanBlock, | 				action: EasyMDE.cleanBlock, | ||||||
|         className: 'fa fa-eraser fa-clean-block', | 				className: "fa fa-eraser fa-clean-block", | ||||||
|         title: gettext('Clean block') | 				title: gettext("Clean block"), | ||||||
| 			}, | 			}, | ||||||
|       '|', | 			"|", | ||||||
| 			{ | 			{ | ||||||
|         name: 'preview', | 				name: "preview", | ||||||
| 				action: EasyMDE.togglePreview, | 				action: EasyMDE.togglePreview, | ||||||
|         className: 'fa fa-eye no-disable', | 				className: "fa fa-eye no-disable", | ||||||
|         title: gettext('Toggle preview') | 				title: gettext("Toggle preview"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'side-by-side', | 				name: "side-by-side", | ||||||
| 				action: EasyMDE.toggleSideBySide, | 				action: EasyMDE.toggleSideBySide, | ||||||
|         className: 'fa fa-columns no-disable no-mobile', | 				className: "fa fa-columns no-disable no-mobile", | ||||||
|         title: gettext('Toggle side by side') | 				title: gettext("Toggle side by side"), | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         name: 'fullscreen', | 				name: "fullscreen", | ||||||
| 				action: EasyMDE.toggleFullScreen, | 				action: EasyMDE.toggleFullScreen, | ||||||
|         className: 'fa fa-expand no-mobile', | 				className: "fa fa-expand no-mobile", | ||||||
|         title: gettext('Toggle fullscreen') | 				title: gettext("Toggle fullscreen"), | ||||||
| 			}, | 			}, | ||||||
|       '|', | 			"|", | ||||||
| 			{ | 			{ | ||||||
|         name: 'guide', | 				name: "guide", | ||||||
|         action: '/page/Aide_sur_la_syntaxe', | 				action: "/page/Aide_sur_la_syntaxe", | ||||||
|         className: 'fa fa-question-circle', | 				className: "fa fa-question-circle", | ||||||
|         title: gettext('Markdown guide') | 				title: gettext("Markdown guide"), | ||||||
|       } | 			}, | ||||||
|     ] | 		], | ||||||
|   }) | 	}); | ||||||
|  |  | ||||||
| 	const submits = textarea | 	const submits = textarea | ||||||
|     .closest('form') | 		.closest("form") | ||||||
|     .querySelectorAll('input[type="submit"]') | 		.querySelectorAll('input[type="submit"]'); | ||||||
|   const parentDiv = textarea.parentElement | 	const parentDiv = textarea.parentElement; | ||||||
|   let submitPressed = false | 	let submitPressed = false; | ||||||
|  |  | ||||||
|   function checkMarkdownInput (e) { | 	function checkMarkdownInput(e) { | ||||||
| 		// an attribute is null if it does not exist, else a string | 		// an attribute is null if it does not exist, else a string | ||||||
|     const required = textarea.getAttribute('required') != null | 		const required = textarea.getAttribute("required") != null; | ||||||
|     const length = textarea.value.trim().length | 		const length = textarea.value.trim().length; | ||||||
|  |  | ||||||
| 		if (required && length === 0) { | 		if (required && length === 0) { | ||||||
|       parentDiv.style.boxShadow = 'red 0px 0px 1.5px 1px' | 			parentDiv.style.boxShadow = "red 0px 0px 1.5px 1px"; | ||||||
| 		} else { | 		} else { | ||||||
|       parentDiv.style.boxShadow = '' | 			parentDiv.style.boxShadow = ""; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   function onSubmitClick (e) { | 	function onSubmitClick(e) { | ||||||
| 		if (!submitPressed) { | 		if (!submitPressed) { | ||||||
|       easymde.codemirror.on('change', checkMarkdownInput) | 			easymde.codemirror.on("change", checkMarkdownInput); | ||||||
| 		} | 		} | ||||||
|     submitPressed = true | 		submitPressed = true; | ||||||
|     checkMarkdownInput(e) | 		checkMarkdownInput(e); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   submits.forEach((submit) => { | 	for (const submit of submits) { | ||||||
|     submit.addEventListener('click', onSubmitClick) | 		submit.addEventListener("click", onSubmitClick); | ||||||
|   }) | 	} | ||||||
| }; | } | ||||||
|  |  | ||||||
| window.easymdeFactory = easymdeFactory | window.easymdeFactory = easymdeFactory; | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| require('@fortawesome/fontawesome-free/css/all.css') | require("@fortawesome/fontawesome-free/css/all.css"); | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								core/static/webpack/jquery-index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								core/static/webpack/jquery-index.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,25 +1,25 @@ | |||||||
| import $ from 'jquery' | import $ from "jquery"; | ||||||
| import 'jquery.shorten/src/jquery.shorten.min.js' | import "jquery.shorten/src/jquery.shorten.min.js"; | ||||||
|  |  | ||||||
| // We ship jquery-ui with jquery because when standalone with webpack | // We ship jquery-ui with jquery because when standalone with webpack | ||||||
| // JQuery is also included in the jquery-ui package. We do gain space by doing this | // JQuery is also included in the jquery-ui package. We do gain space by doing this | ||||||
| // We require jquery-ui components manually and not in a loop | // We require jquery-ui components manually and not in a loop | ||||||
| // Otherwise it increases the output files by a x2 factor ! | // Otherwise it increases the output files by a x2 factor ! | ||||||
| require('jquery-ui/ui/widgets/accordion.js') | require("jquery-ui/ui/widgets/accordion.js"); | ||||||
| require('jquery-ui/ui/widgets/autocomplete.js') | require("jquery-ui/ui/widgets/autocomplete.js"); | ||||||
| require('jquery-ui/ui/widgets/button.js') | require("jquery-ui/ui/widgets/button.js"); | ||||||
| require('jquery-ui/ui/widgets/dialog.js') | require("jquery-ui/ui/widgets/dialog.js"); | ||||||
| require('jquery-ui/ui/widgets/tabs.js') | require("jquery-ui/ui/widgets/tabs.js"); | ||||||
|  |  | ||||||
| require('jquery-ui/themes/base/all.css') | require("jquery-ui/themes/base/all.css"); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Simple wrapper to solve shorten not being able on legacy pages |  * Simple wrapper to solve shorten not being able on legacy pages | ||||||
|  * @param {string} selector to be passed to jQuery |  * @param {string} selector to be passed to jQuery | ||||||
|  * @param {Object} options object to pass to the shorten function |  * @param {Object} options object to pass to the shorten function | ||||||
|  **/ |  **/ | ||||||
| export function shorten (selector, options) { | function shorten(selector, options) { | ||||||
|   $(selector).shorten(options) | 	$(selector).shorten(options); | ||||||
| } | } | ||||||
|  |  | ||||||
| window.shorten = shorten | window.shorten = shorten; | ||||||
|   | |||||||
| @@ -1,79 +1,80 @@ | |||||||
| /* eslint-disable camelcase */ | document.addEventListener("alpine:init", () => { | ||||||
| /* global basket, click_api_url, csrf_token, products_autocomplete */ | 	Alpine.data("counter", () => ({ | ||||||
| document.addEventListener('alpine:init', () => { |  | ||||||
|   Alpine.data('counter', () => ({ |  | ||||||
| 		basket, | 		basket, | ||||||
| 		errors: [], | 		errors: [], | ||||||
|  |  | ||||||
|     sum_basket () { | 		sum_basket() { | ||||||
| 			if (!this.basket || Object.keys(this.basket).length === 0) { | 			if (!this.basket || Object.keys(this.basket).length === 0) { | ||||||
|         return 0 | 				return 0; | ||||||
| 			} | 			} | ||||||
|       const total = Object.values(this.basket) | 			const total = Object.values(this.basket).reduce( | ||||||
|         .reduce((acc, cur) => acc + cur.qty * cur.price, 0) | 				(acc, cur) => acc + cur.qty * cur.price, | ||||||
|       return total / 100 | 				0, | ||||||
|  | 			); | ||||||
|  | 			return total / 100; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async handle_code (event) { | 		async handle_code(event) { | ||||||
|       const code = $(event.target).find('#code_field').val().toUpperCase() | 			const code = $(event.target).find("#code_field").val().toUpperCase(); | ||||||
|       if (['FIN', 'ANN'].includes(code)) { | 			if (["FIN", "ANN"].includes(code)) { | ||||||
|         $(event.target).submit() | 				$(event.target).submit(); | ||||||
| 			} else { | 			} else { | ||||||
|         await this.handle_action(event) | 				await this.handle_action(event); | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async handle_action (event) { | 		async handle_action(event) { | ||||||
|       const payload = $(event.target).serialize() | 			const payload = $(event.target).serialize(); | ||||||
| 			const request = new Request(click_api_url, { | 			const request = new Request(click_api_url, { | ||||||
|         method: 'POST', | 				method: "POST", | ||||||
| 				body: payload, | 				body: payload, | ||||||
| 				headers: { | 				headers: { | ||||||
|           Accept: 'application/json', | 					Accept: "application/json", | ||||||
|           'X-CSRFToken': csrf_token | 					"X-CSRFToken": csrf_token, | ||||||
|         } | 				}, | ||||||
|       }) | 			}); | ||||||
|       const response = await fetch(request) | 			const response = await fetch(request); | ||||||
|       const json = await response.json() | 			const json = await response.json(); | ||||||
|       this.basket = json.basket | 			this.basket = json.basket; | ||||||
|       this.errors = json.errors | 			this.errors = json.errors; | ||||||
|       $('form.code_form #code_field').val('').focus() | 			$("form.code_form #code_field").val("").focus(); | ||||||
|     } | 		}, | ||||||
|   })) | 	})); | ||||||
| }) | }); | ||||||
|  |  | ||||||
| $(function () { | $(() => { | ||||||
| 	/* Autocompletion in the code field */ | 	/* Autocompletion in the code field */ | ||||||
|   const code_field = $('#code_field') | 	const code_field = $("#code_field"); | ||||||
|  |  | ||||||
|   let quantity = '' | 	let quantity = ""; | ||||||
| 	code_field.autocomplete({ | 	code_field.autocomplete({ | ||||||
|     select: function (event, ui) { | 		select: (event, ui) => { | ||||||
|       event.preventDefault() | 			event.preventDefault(); | ||||||
|       code_field.val(quantity + ui.item.value) | 			code_field.val(quantity + ui.item.value); | ||||||
| 		}, | 		}, | ||||||
|     focus: function (event, ui) { | 		focus: (event, ui) => { | ||||||
|       event.preventDefault() | 			event.preventDefault(); | ||||||
|       code_field.val(quantity + ui.item.value) | 			code_field.val(quantity + ui.item.value); | ||||||
| 		}, | 		}, | ||||||
|     source: function (request, response) { | 		source: (request, response) => { | ||||||
|       const res = /^(\d+x)?(.*)/i.exec(request.term) | 			const res = /^(\d+x)?(.*)/i.exec(request.term); | ||||||
|       quantity = res[1] || '' | 			quantity = res[1] || ""; | ||||||
|       const search = res[2] | 			const search = res[2]; | ||||||
|       const matcher = new RegExp($.ui.autocomplete.escapeRegex(search), 'i') | 			const matcher = new RegExp($.ui.autocomplete.escapeRegex(search), "i"); | ||||||
|       response($.grep(products_autocomplete, function (value) { | 			response( | ||||||
|         value = value.tags | 				$.grep(products_autocomplete, (value) => { | ||||||
|         return matcher.test(value) | 					return matcher.test(value.tags); | ||||||
|       })) | 				}), | ||||||
|     } | 			); | ||||||
|   }) | 		}, | ||||||
|  | 	}); | ||||||
|  |  | ||||||
| 	/* Accordion UI between basket and refills */ | 	/* Accordion UI between basket and refills */ | ||||||
|   $('#click_form').accordion({ | 	$("#click_form").accordion({ | ||||||
|     heightStyle: 'content', | 		heightStyle: "content", | ||||||
|     activate: () => $('.focus').focus() | 		activate: () => $(".focus").focus(), | ||||||
|   }) | 	}); | ||||||
|   $('#products').tabs() | 	$("#products").tabs(); | ||||||
|  |  | ||||||
|   code_field.focus() | 	code_field.focus(); | ||||||
| }) | }); | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ | |||||||
| #eboutic .item-list li { | #eboutic .item-list li { | ||||||
| 	display: flex; | 	display: flex; | ||||||
| 	align-items: center; | 	align-items: center; | ||||||
|     margin-bottom: 10px | 	margin-bottom: 10px; | ||||||
| } | } | ||||||
|  |  | ||||||
| #eboutic .item-row { | #eboutic .item-row { | ||||||
| @@ -236,6 +236,10 @@ | |||||||
| } | } | ||||||
|  |  | ||||||
| @keyframes bg-in-out { | @keyframes bg-in-out { | ||||||
|     0%   { background-color: white; }  | 	0% { | ||||||
|     100% { background-color: rgb(216, 236, 255); } | 		background-color: white; | ||||||
|  | 	} | ||||||
|  | 	100% { | ||||||
|  | 		background-color: rgb(216, 236, 255); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| @@ -1,4 +1,3 @@ | |||||||
| /* eslint-disable camelcase */ |  | ||||||
| /** | /** | ||||||
|  * @typedef {Object} BasketItem An item in the basket |  * @typedef {Object} BasketItem An item in the basket | ||||||
|  * @property {number} id The id of the product |  * @property {number} id The id of the product | ||||||
| @@ -7,98 +6,102 @@ | |||||||
|  * @property {number} unit_price The unit price of the product |  * @property {number} unit_price The unit price of the product | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| const BASKET_ITEMS_COOKIE_NAME = 'basket_items' | const BASKET_ITEMS_COOKIE_NAME = "basket_items"; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Search for a cookie by name |  * Search for a cookie by name | ||||||
|  * @param {string} name Name of the cookie to get |  * @param {string} name Name of the cookie to get | ||||||
|  * @returns {string|null|undefined} the value of the cookie or null if it does not exist, undefined if not found |  * @returns {string|null|undefined} the value of the cookie or null if it does not exist, undefined if not found | ||||||
|  */ |  */ | ||||||
| function getCookie (name) { | function getCookie(name) { | ||||||
|   if (!document.cookie || document.cookie.length === 0) return null | 	if (!document.cookie || document.cookie.length === 0) return null; | ||||||
|  |  | ||||||
| 	const found = document.cookie | 	const found = document.cookie | ||||||
|     .split(';') | 		.split(";") | ||||||
|     .map(c => c.trim()) | 		.map((c) => c.trim()) | ||||||
|     .find(c => c.startsWith(name + '=')) | 		.find((c) => c.startsWith(`${name}=`)); | ||||||
|  |  | ||||||
|   return found === undefined ? undefined : decodeURIComponent(found.split('=')[1]) | 	return found === undefined | ||||||
|  | 		? undefined | ||||||
|  | 		: decodeURIComponent(found.split("=")[1]); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Fetch the basket items from the associated cookie |  * Fetch the basket items from the associated cookie | ||||||
|  * @returns {BasketItem[]|[]} the items in the basket |  * @returns {BasketItem[]|[]} the items in the basket | ||||||
|  */ |  */ | ||||||
| function get_starting_items () { | function get_starting_items() { | ||||||
|   const cookie = getCookie(BASKET_ITEMS_COOKIE_NAME) | 	const cookie = getCookie(BASKET_ITEMS_COOKIE_NAME); | ||||||
| 	if (!cookie) { | 	if (!cookie) { | ||||||
|     return [] | 		return []; | ||||||
| 	} | 	} | ||||||
| 	// Django cookie backend converts `,` to `\054` | 	// Django cookie backend converts `,` to `\054` | ||||||
|   let parsed = JSON.parse(cookie.replace(/\\054/g, ',')) | 	let parsed = JSON.parse(cookie.replace(/\\054/g, ",")); | ||||||
|   if (typeof parsed === 'string') { | 	if (typeof parsed === "string") { | ||||||
| 		// In some conditions, a second parsing is needed | 		// In some conditions, a second parsing is needed | ||||||
|     parsed = JSON.parse(parsed) | 		parsed = JSON.parse(parsed); | ||||||
| 	} | 	} | ||||||
|   const res = Array.isArray(parsed) ? parsed : [] | 	const res = Array.isArray(parsed) ? parsed : []; | ||||||
|   return res.filter((i) => !!document.getElementById(i.id)) | 	return res.filter((i) => !!document.getElementById(i.id)); | ||||||
| } | } | ||||||
|  |  | ||||||
| document.addEventListener('alpine:init', () => { | document.addEventListener("alpine:init", () => { | ||||||
|   Alpine.data('basket', () => ({ | 	Alpine.data("basket", () => ({ | ||||||
| 		items: get_starting_items(), | 		items: get_starting_items(), | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Get the total price of the basket | 		 * Get the total price of the basket | ||||||
| 		 * @returns {number} The total price of the basket | 		 * @returns {number} The total price of the basket | ||||||
| 		 */ | 		 */ | ||||||
|     get_total () { | 		get_total() { | ||||||
|       return this.items | 			return this.items.reduce( | ||||||
|         .reduce((acc, item) => acc + item.quantity * item.unit_price, 0) | 				(acc, item) => acc + item.quantity * item.unit_price, | ||||||
|  | 				0, | ||||||
|  | 			); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Add 1 to the quantity of an item in the basket | 		 * Add 1 to the quantity of an item in the basket | ||||||
| 		 * @param {BasketItem} item | 		 * @param {BasketItem} item | ||||||
| 		 */ | 		 */ | ||||||
|     add (item) { | 		add(item) { | ||||||
|       item.quantity++ | 			item.quantity++; | ||||||
|       this.set_cookies() | 			this.set_cookies(); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Remove 1 to the quantity of an item in the basket | 		 * Remove 1 to the quantity of an item in the basket | ||||||
| 		 * @param {BasketItem} item_id | 		 * @param {BasketItem} item_id | ||||||
| 		 */ | 		 */ | ||||||
|     remove (item_id) { | 		remove(item_id) { | ||||||
|       const index = this.items.findIndex(e => e.id === item_id) | 			const index = this.items.findIndex((e) => e.id === item_id); | ||||||
|  |  | ||||||
|       if (index < 0) return | 			if (index < 0) return; | ||||||
|       this.items[index].quantity -= 1 | 			this.items[index].quantity -= 1; | ||||||
|  |  | ||||||
| 			if (this.items[index].quantity === 0) { | 			if (this.items[index].quantity === 0) { | ||||||
|         this.items = this.items.filter((e) => e.id !== this.items[index].id) | 				this.items = this.items.filter((e) => e.id !== this.items[index].id); | ||||||
| 			} | 			} | ||||||
|       this.set_cookies() | 			this.set_cookies(); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Remove all the items from the basket & cleans the catalog CSS classes | 		 * Remove all the items from the basket & cleans the catalog CSS classes | ||||||
| 		 */ | 		 */ | ||||||
|     clear_basket () { | 		clear_basket() { | ||||||
|       this.items = [] | 			this.items = []; | ||||||
|       this.set_cookies() | 			this.set_cookies(); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Set the cookie in the browser with the basket items | 		 * Set the cookie in the browser with the basket items | ||||||
| 		 * ! the cookie survives an hour | 		 * ! the cookie survives an hour | ||||||
| 		 */ | 		 */ | ||||||
|     set_cookies () { | 		set_cookies() { | ||||||
| 			if (this.items.length === 0) { | 			if (this.items.length === 0) { | ||||||
|         document.cookie = `${BASKET_ITEMS_COOKIE_NAME}=;Max-Age=0` | 				document.cookie = `${BASKET_ITEMS_COOKIE_NAME}=;Max-Age=0`; | ||||||
| 			} else { | 			} else { | ||||||
|         document.cookie = `${BASKET_ITEMS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(this.items))};Max-Age=3600` | 				document.cookie = `${BASKET_ITEMS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(this.items))};Max-Age=3600`; | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| @@ -109,18 +112,18 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		 * @param {number} price The unit price of the product | 		 * @param {number} price The unit price of the product | ||||||
| 		 * @returns {BasketItem} The created item | 		 * @returns {BasketItem} The created item | ||||||
| 		 */ | 		 */ | ||||||
|     create_item (id, name, price) { | 		create_item(id, name, price) { | ||||||
| 			const new_item = { | 			const new_item = { | ||||||
| 				id, | 				id, | ||||||
| 				name, | 				name, | ||||||
| 				quantity: 0, | 				quantity: 0, | ||||||
|         unit_price: price | 				unit_price: price, | ||||||
|       } | 			}; | ||||||
|  |  | ||||||
|       this.items.push(new_item) | 			this.items.push(new_item); | ||||||
|       this.add(new_item) | 			this.add(new_item); | ||||||
|  |  | ||||||
|       return new_item | 			return new_item; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| @@ -130,16 +133,16 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		 * @param {string} name The name of the product | 		 * @param {string} name The name of the product | ||||||
| 		 * @param {number} price The unit price of the product | 		 * @param {number} price The unit price of the product | ||||||
| 		 */ | 		 */ | ||||||
|     add_from_catalog (id, name, price) { | 		add_from_catalog(id, name, price) { | ||||||
|       let item = this.items.find(e => e.id === id) | 			let item = this.items.find((e) => e.id === id); | ||||||
|  |  | ||||||
| 			// if the item is not in the basket, we create it | 			// if the item is not in the basket, we create it | ||||||
| 			// else we add + 1 to it | 			// else we add + 1 to it | ||||||
| 			if (!item) { | 			if (!item) { | ||||||
|         item = this.create_item(id, name, price) | 				item = this.create_item(id, name, price); | ||||||
| 			} else { | 			} else { | ||||||
|         this.add(item) | 				this.add(item); | ||||||
| 			} | 			} | ||||||
|     } | 		}, | ||||||
|   })) | 	})); | ||||||
| }) | }); | ||||||
|   | |||||||
| @@ -1,6 +1,3 @@ | |||||||
| /* global et_data, et_data_url, billing_info_url, |  | ||||||
|   billing_info_success_message, billing_info_failure_message */ |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @readonly |  * @readonly | ||||||
|  * @enum {number} |  * @enum {number} | ||||||
| @@ -8,75 +5,75 @@ | |||||||
| const BillingInfoReqState = { | const BillingInfoReqState = { | ||||||
| 	SUCCESS: 1, | 	SUCCESS: 1, | ||||||
| 	FAILURE: 2, | 	FAILURE: 2, | ||||||
|   SENDING: 3 | 	SENDING: 3, | ||||||
| } | }; | ||||||
|  |  | ||||||
| document.addEventListener('alpine:init', () => { | document.addEventListener("alpine:init", () => { | ||||||
|   Alpine.store('billing_inputs', { | 	Alpine.store("billing_inputs", { | ||||||
| 		data: et_data, | 		data: et_data, | ||||||
|  |  | ||||||
|     async fill () { | 		async fill() { | ||||||
|       document.getElementById('bank-submit-button').disabled = true | 			document.getElementById("bank-submit-button").disabled = true; | ||||||
|       const res = await fetch(et_data_url) | 			const res = await fetch(et_data_url); | ||||||
| 			if (res.ok) { | 			if (res.ok) { | ||||||
|         this.data = await res.json() | 				this.data = await res.json(); | ||||||
|         document.getElementById('bank-submit-button').disabled = false | 				document.getElementById("bank-submit-button").disabled = false; | ||||||
| 			} | 			} | ||||||
|     } | 		}, | ||||||
|   }) | 	}); | ||||||
|  |  | ||||||
|   Alpine.data('billing_infos', () => ({ | 	Alpine.data("billing_infos", () => ({ | ||||||
| 		/** @type {BillingInfoReqState | null} */ | 		/** @type {BillingInfoReqState | null} */ | ||||||
| 		req_state: null, | 		req_state: null, | ||||||
|  |  | ||||||
|     async send_form () { | 		async send_form() { | ||||||
|       this.req_state = BillingInfoReqState.SENDING | 			this.req_state = BillingInfoReqState.SENDING; | ||||||
|       const form = document.getElementById('billing_info_form') | 			const form = document.getElementById("billing_info_form"); | ||||||
|       document.getElementById('bank-submit-button').disabled = true | 			document.getElementById("bank-submit-button").disabled = true; | ||||||
| 			const payload = Object.fromEntries( | 			const payload = Object.fromEntries( | ||||||
|         Array.from(form.querySelectorAll('input, select')) | 				Array.from(form.querySelectorAll("input, select")) | ||||||
|           .filter((elem) => elem.type !== 'submit' && elem.value) | 					.filter((elem) => elem.type !== "submit" && elem.value) | ||||||
|           .map((elem) => [elem.name, elem.value]) | 					.map((elem) => [elem.name, elem.value]), | ||||||
|       ) | 			); | ||||||
| 			const res = await fetch(billing_info_url, { | 			const res = await fetch(billing_info_url, { | ||||||
|         method: 'PUT', | 				method: "PUT", | ||||||
|         body: JSON.stringify(payload) | 				body: JSON.stringify(payload), | ||||||
|       }) | 			}); | ||||||
| 			this.req_state = res.ok | 			this.req_state = res.ok | ||||||
| 				? BillingInfoReqState.SUCCESS | 				? BillingInfoReqState.SUCCESS | ||||||
|         : BillingInfoReqState.FAILURE | 				: BillingInfoReqState.FAILURE; | ||||||
| 			if (res.status === 422) { | 			if (res.status === 422) { | ||||||
|         const errors = (await res.json()).detail.map((err) => err.loc).flat() | 				const errors = (await res.json()).detail.flatMap((err) => err.loc); | ||||||
|         Array.from(form.querySelectorAll('input')) | 				for (const elem of Array.from(form.querySelectorAll("input")).filter( | ||||||
|           .filter((elem) => errors.includes(elem.name)) | 					(elem) => errors.includes(elem.name), | ||||||
|           .forEach((elem) => { | 				)) { | ||||||
|             elem.setCustomValidity(gettext('Incorrect value')) | 					elem.setCustomValidity(gettext("Incorrect value")); | ||||||
|             elem.reportValidity() | 					elem.reportValidity(); | ||||||
|             elem.oninput = () => elem.setCustomValidity('') | 					elem.oninput = () => elem.setCustomValidity(""); | ||||||
|           }) | 				} | ||||||
| 			} else if (res.ok) { | 			} else if (res.ok) { | ||||||
|         Alpine.store('billing_inputs').fill() | 				Alpine.store("billing_inputs").fill(); | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     get_alert_color () { | 		get_alert_color() { | ||||||
| 			if (this.req_state === BillingInfoReqState.SUCCESS) { | 			if (this.req_state === BillingInfoReqState.SUCCESS) { | ||||||
|         return 'green' | 				return "green"; | ||||||
| 			} | 			} | ||||||
| 			if (this.req_state === BillingInfoReqState.FAILURE) { | 			if (this.req_state === BillingInfoReqState.FAILURE) { | ||||||
|         return 'red' | 				return "red"; | ||||||
| 			} | 			} | ||||||
|       return '' | 			return ""; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     get_alert_message () { | 		get_alert_message() { | ||||||
| 			if (this.req_state === BillingInfoReqState.SUCCESS) { | 			if (this.req_state === BillingInfoReqState.SUCCESS) { | ||||||
|         return billing_info_success_message | 				return billing_info_success_message; | ||||||
| 			} | 			} | ||||||
| 			if (this.req_state === BillingInfoReqState.FAILURE) { | 			if (this.req_state === BillingInfoReqState.FAILURE) { | ||||||
|         return billing_info_failure_message | 				return billing_info_failure_message; | ||||||
| 			} | 			} | ||||||
|       return '' | 			return ""; | ||||||
|     } | 		}, | ||||||
|   })) | 	})); | ||||||
| }) | }); | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										3294
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3294
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										13
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								package.json
									
									
									
									
									
								
							| @@ -11,26 +11,17 @@ | |||||||
| 	"keywords": [], | 	"keywords": [], | ||||||
| 	"author": "", | 	"author": "", | ||||||
| 	"license": "GPL-3.0-only", | 	"license": "GPL-3.0-only", | ||||||
|     "sideEffects": [ | 	"sideEffects": [".css"], | ||||||
|         ".css" |  | ||||||
|     ], |  | ||||||
|     "standard": { |  | ||||||
|       "ignore": [ |  | ||||||
|         "core/static/vendored", |  | ||||||
|         "staticfiles/generated" |  | ||||||
|       ], |  | ||||||
|       "globals": [ "Alpine", "$", "jQuery", "gettext", "interpolate" ] |  | ||||||
|     }, |  | ||||||
| 	"devDependencies": { | 	"devDependencies": { | ||||||
| 		"@babel/core": "^7.25.2", | 		"@babel/core": "^7.25.2", | ||||||
| 		"@babel/preset-env": "^7.25.4", | 		"@babel/preset-env": "^7.25.4", | ||||||
|  | 		"@biomejs/biome": "1.9.3", | ||||||
| 		"babel-loader": "^9.2.1", | 		"babel-loader": "^9.2.1", | ||||||
| 		"css-loader": "^7.1.2", | 		"css-loader": "^7.1.2", | ||||||
| 		"css-minimizer-webpack-plugin": "^7.0.0", | 		"css-minimizer-webpack-plugin": "^7.0.0", | ||||||
| 		"expose-loader": "^5.0.0", | 		"expose-loader": "^5.0.0", | ||||||
| 		"mini-css-extract-plugin": "^2.9.1", | 		"mini-css-extract-plugin": "^2.9.1", | ||||||
| 		"source-map-loader": "^5.0.0", | 		"source-map-loader": "^5.0.0", | ||||||
|         "standard": "^17.1.2", |  | ||||||
| 		"terser-webpack-plugin": "^5.3.10", | 		"terser-webpack-plugin": "^5.3.10", | ||||||
| 		"webpack": "^5.94.0", | 		"webpack": "^5.94.0", | ||||||
| 		"webpack-cli": "^5.1.4" | 		"webpack-cli": "^5.1.4" | ||||||
|   | |||||||
| @@ -1,7 +1,3 @@ | |||||||
| /* eslint-disable camelcase */ |  | ||||||
| /* global Image, History, fetch_paginated, picture_endpoint, |  | ||||||
|   sithSelect2, remote_data_source, first_picture_id, |  | ||||||
|   album_url, user_is_sas_admin, user_id */ |  | ||||||
| /** | /** | ||||||
|  * @typedef PictureIdentification |  * @typedef PictureIdentification | ||||||
|  * @property {number} id The actual id of the identification |  * @property {number} id The actual id of the identification | ||||||
| @@ -13,22 +9,22 @@ | |||||||
|  * able to prefetch its data. |  * able to prefetch its data. | ||||||
|  */ |  */ | ||||||
| class PictureWithIdentifications { | class PictureWithIdentifications { | ||||||
|   identifications = null | 	identifications = null; | ||||||
|   image_loading = false | 	image_loading = false; | ||||||
|   identifications_loading = false | 	identifications_loading = false; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {Picture} picture | 	 * @param {Picture} picture | ||||||
| 	 */ | 	 */ | ||||||
|   constructor (picture) { | 	constructor(picture) { | ||||||
|     Object.assign(this, picture) | 		Object.assign(this, picture); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {Picture} picture | 	 * @param {Picture} picture | ||||||
| 	 */ | 	 */ | ||||||
|   static from_picture (picture) { | 	static from_picture(picture) { | ||||||
|     return new PictureWithIdentifications(picture) | 		return new PictureWithIdentifications(picture); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| @@ -37,40 +33,40 @@ class PictureWithIdentifications { | |||||||
| 	 * @param {?Object=} options | 	 * @param {?Object=} options | ||||||
| 	 * @return {Promise<void>} | 	 * @return {Promise<void>} | ||||||
| 	 */ | 	 */ | ||||||
|   async load_identifications (options) { | 	async load_identifications(options) { | ||||||
| 		if (this.identifications_loading) { | 		if (this.identifications_loading) { | ||||||
|       return // The users are already being fetched. | 			return; // The users are already being fetched. | ||||||
| 		} | 		} | ||||||
| 		if (!!this.identifications && !options?.force_reload) { | 		if (!!this.identifications && !options?.force_reload) { | ||||||
| 			// The users are already fetched | 			// The users are already fetched | ||||||
| 			// and the user does not want to force the reload | 			// and the user does not want to force the reload | ||||||
|       return | 			return; | ||||||
| 		} | 		} | ||||||
|     this.identifications_loading = true | 		this.identifications_loading = true; | ||||||
|     const url = `/api/sas/picture/${this.id}/identified` | 		const url = `/api/sas/picture/${this.id}/identified`; | ||||||
|     this.identifications = await (await fetch(url)).json() | 		this.identifications = await (await fetch(url)).json(); | ||||||
|     this.identifications_loading = false | 		this.identifications_loading = false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Preload the photo and the identifications | 	 * Preload the photo and the identifications | ||||||
| 	 * @return {Promise<void>} | 	 * @return {Promise<void>} | ||||||
| 	 */ | 	 */ | ||||||
|   async preload () { | 	async preload() { | ||||||
|     const img = new Image() | 		const img = new Image(); | ||||||
|     img.src = this.compressed_url | 		img.src = this.compressed_url; | ||||||
| 		if (!img.complete) { | 		if (!img.complete) { | ||||||
|       this.image_loading = true | 			this.image_loading = true; | ||||||
|       img.addEventListener('load', () => { | 			img.addEventListener("load", () => { | ||||||
|         this.image_loading = false | 				this.image_loading = false; | ||||||
|       }) | 			}); | ||||||
| 		} | 		} | ||||||
|     await this.load_identifications() | 		await this.load_identifications(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| document.addEventListener('alpine:init', () => { | document.addEventListener("alpine:init", () => { | ||||||
|   Alpine.data('picture_viewer', () => ({ | 	Alpine.data("picture_viewer", () => ({ | ||||||
| 		/** | 		/** | ||||||
| 		 * All the pictures that can be displayed on this picture viewer | 		 * All the pictures that can be displayed on this picture viewer | ||||||
| 		 * @type PictureWithIdentifications[] | 		 * @type PictureWithIdentifications[] | ||||||
| @@ -85,14 +81,14 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		current_picture: { | 		current_picture: { | ||||||
| 			is_moderated: true, | 			is_moderated: true, | ||||||
| 			id: null, | 			id: null, | ||||||
|       name: '', | 			name: "", | ||||||
|       display_name: '', | 			display_name: "", | ||||||
|       compressed_url: '', | 			compressed_url: "", | ||||||
|       profile_url: '', | 			profile_url: "", | ||||||
|       full_size_url: '', | 			full_size_url: "", | ||||||
|       owner: '', | 			owner: "", | ||||||
| 			date: new Date(), | 			date: new Date(), | ||||||
|       identifications: [] | 			identifications: [], | ||||||
| 		}, | 		}, | ||||||
| 		/** | 		/** | ||||||
| 		 * The picture which will be displayed next if the user press the "next" button | 		 * The picture which will be displayed next if the user press the "next" button | ||||||
| @@ -115,7 +111,7 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		 * Error message when a moderation operation fails | 		 * Error message when a moderation operation fails | ||||||
| 		 * @type string | 		 * @type string | ||||||
| 		 **/ | 		 **/ | ||||||
|     moderation_error: '', | 		moderation_error: "", | ||||||
| 		/** | 		/** | ||||||
| 		 * Method of pushing new url to the browser history | 		 * Method of pushing new url to the browser history | ||||||
| 		 * Used by popstate event and always reset to it's default value when used | 		 * Used by popstate event and always reset to it's default value when used | ||||||
| @@ -123,42 +119,43 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		 **/ | 		 **/ | ||||||
| 		pushstate: History.PUSH, | 		pushstate: History.PUSH, | ||||||
|  |  | ||||||
|     async init () { | 		async init() { | ||||||
| 			this.pictures = (await fetch_paginated(picture_endpoint)).map( | 			this.pictures = (await fetch_paginated(picture_endpoint)).map( | ||||||
|         PictureWithIdentifications.from_picture | 				PictureWithIdentifications.from_picture, | ||||||
|       ) | 			); | ||||||
| 			this.selector = sithSelect2({ | 			this.selector = sithSelect2({ | ||||||
| 				element: $(this.$refs.search), | 				element: $(this.$refs.search), | ||||||
|         data_source: remote_data_source('/api/user/search', { | 				data_source: remote_data_source("/api/user/search", { | ||||||
| 					excluded: () => [ | 					excluded: () => [ | ||||||
| 						...(this.current_picture.identifications || []).map( | 						...(this.current_picture.identifications || []).map( | ||||||
|               (i) => i.user.id | 							(i) => i.user.id, | ||||||
|             ) | 						), | ||||||
| 					], | 					], | ||||||
|           result_converter: (obj) => Object({ ...obj, text: obj.display_name }) | 					result_converter: (obj) => Object({ ...obj, text: obj.display_name }), | ||||||
| 				}), | 				}), | ||||||
|         picture_getter: (user) => user.profile_pict | 				picture_getter: (user) => user.profile_pict, | ||||||
|       }) | 			}); | ||||||
| 			this.current_picture = this.pictures.find( | 			this.current_picture = this.pictures.find( | ||||||
|         (i) => i.id === first_picture_id | 				(i) => i.id === first_picture_id, | ||||||
|       ) | 			); | ||||||
|       this.$watch('current_picture', (current, previous) => { | 			this.$watch("current_picture", (current, previous) => { | ||||||
|         if (current === previous) { /* Avoid recursive updates */ | 				if (current === previous) { | ||||||
|           return | 					/* Avoid recursive updates */ | ||||||
|  | 					return; | ||||||
| 				} | 				} | ||||||
|         this.update_picture() | 				this.update_picture(); | ||||||
|       }) | 			}); | ||||||
|       window.addEventListener('popstate', async (event) => { | 			window.addEventListener("popstate", async (event) => { | ||||||
| 				if (!event.state || event.state.sas_picture_id === undefined) { | 				if (!event.state || event.state.sas_picture_id === undefined) { | ||||||
|           return | 					return; | ||||||
| 				} | 				} | ||||||
|         this.pushstate = History.REPLACE | 				this.pushstate = History.REPLACE; | ||||||
| 				this.current_picture = this.pictures.find( | 				this.current_picture = this.pictures.find( | ||||||
|           (i) => i.id === parseInt(event.state.sas_picture_id) | 					(i) => i.id === Number.parseInt(event.state.sas_picture_id), | ||||||
|         ) | 				); | ||||||
|       }) | 			}); | ||||||
|       this.pushstate = History.REPLACE /* Avoid first url push */ | 			this.pushstate = History.REPLACE; /* Avoid first url push */ | ||||||
|       await this.update_picture() | 			await this.update_picture(); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| @@ -169,77 +166,78 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		 * and the previous picture, the next picture and | 		 * and the previous picture, the next picture and | ||||||
| 		 * the list of identified users are updated. | 		 * the list of identified users are updated. | ||||||
| 		 */ | 		 */ | ||||||
|     async update_picture () { | 		async update_picture() { | ||||||
| 			const update_args = [ | 			const update_args = [ | ||||||
| 				{ sas_picture_id: this.current_picture.id }, | 				{ sas_picture_id: this.current_picture.id }, | ||||||
|         '', | 				"", | ||||||
|         `/sas/picture/${this.current_picture.id}/` | 				`/sas/picture/${this.current_picture.id}/`, | ||||||
|       ] | 			]; | ||||||
| 			if (this.pushstate === History.REPLACE) { | 			if (this.pushstate === History.REPLACE) { | ||||||
|         window.history.replaceState(...update_args) | 				window.history.replaceState(...update_args); | ||||||
|         this.pushstate = History.PUSH | 				this.pushstate = History.PUSH; | ||||||
| 			} else { | 			} else { | ||||||
|         window.history.pushState(...update_args) | 				window.history.pushState(...update_args); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|       this.moderation_error = '' | 			this.moderation_error = ""; | ||||||
|       const index = this.pictures.indexOf(this.current_picture) | 			const index = this.pictures.indexOf(this.current_picture); | ||||||
|       this.previous_picture = this.pictures[index - 1] || null | 			this.previous_picture = this.pictures[index - 1] || null; | ||||||
|       this.next_picture = this.pictures[index + 1] || null | 			this.next_picture = this.pictures[index + 1] || null; | ||||||
|       await this.current_picture.load_identifications() | 			await this.current_picture.load_identifications(); | ||||||
|       this.$refs.main_picture?.addEventListener('load', () => { | 			this.$refs.main_picture?.addEventListener("load", () => { | ||||||
| 				// once the current picture is loaded, | 				// once the current picture is loaded, | ||||||
| 				// start preloading the next and previous pictures | 				// start preloading the next and previous pictures | ||||||
|         this.next_picture?.preload() | 				this.next_picture?.preload(); | ||||||
|         this.previous_picture?.preload() | 				this.previous_picture?.preload(); | ||||||
|       }) | 			}); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async moderate_picture () { | 		async moderate_picture() { | ||||||
| 			const res = await fetch( | 			const res = await fetch( | ||||||
| 				`/api/sas/picture/${this.current_picture.id}/moderate`, | 				`/api/sas/picture/${this.current_picture.id}/moderate`, | ||||||
| 				{ | 				{ | ||||||
|           method: 'PATCH' | 					method: "PATCH", | ||||||
|         } | 				}, | ||||||
|       ) | 			); | ||||||
| 			if (!res.ok) { | 			if (!res.ok) { | ||||||
|         this.moderation_error = `${gettext("Couldn't moderate picture")} : ${res.statusText}` | 				this.moderation_error = `${gettext("Couldn't moderate picture")} : ${res.statusText}`; | ||||||
|         return | 				return; | ||||||
| 			} | 			} | ||||||
|       this.current_picture.is_moderated = true | 			this.current_picture.is_moderated = true; | ||||||
|       this.current_picture.asked_for_removal = false | 			this.current_picture.asked_for_removal = false; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|     async delete_picture () { | 		async delete_picture() { | ||||||
| 			const res = await fetch(`/api/sas/picture/${this.current_picture.id}`, { | 			const res = await fetch(`/api/sas/picture/${this.current_picture.id}`, { | ||||||
|         method: 'DELETE' | 				method: "DELETE", | ||||||
|       }) | 			}); | ||||||
| 			if (!res.ok) { | 			if (!res.ok) { | ||||||
|         this.moderation_error = | 				this.moderation_error = `${gettext("Couldn't delete picture")} : ${res.statusText}`; | ||||||
|           gettext("Couldn't delete picture") + ' : ' + res.statusText | 				return; | ||||||
|         return |  | ||||||
| 			} | 			} | ||||||
|       this.pictures.splice(this.pictures.indexOf(this.current_picture), 1) | 			this.pictures.splice(this.pictures.indexOf(this.current_picture), 1); | ||||||
| 			if (this.pictures.length === 0) { | 			if (this.pictures.length === 0) { | ||||||
| 				// The deleted picture was the only one in the list. | 				// The deleted picture was the only one in the list. | ||||||
| 				// As the album is now empty, go back to the parent page | 				// As the album is now empty, go back to the parent page | ||||||
|         document.location.href = album_url | 				document.location.href = album_url; | ||||||
| 			} | 			} | ||||||
|       this.current_picture = this.next_picture || this.previous_picture | 			this.current_picture = this.next_picture || this.previous_picture; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Send the identification request and update the list of identified users. | 		 * Send the identification request and update the list of identified users. | ||||||
| 		 */ | 		 */ | ||||||
|     async submit_identification () { | 		async submit_identification() { | ||||||
|       const url = `/api/sas/picture/${this.current_picture.id}/identified` | 			const url = `/api/sas/picture/${this.current_picture.id}/identified`; | ||||||
| 			await fetch(url, { | 			await fetch(url, { | ||||||
|         method: 'PUT', | 				method: "PUT", | ||||||
|         body: JSON.stringify(this.selector.val().map((i) => parseInt(i))) | 				body: JSON.stringify( | ||||||
|       }) | 					this.selector.val().map((i) => Number.parseInt(i)), | ||||||
|  | 				), | ||||||
|  | 			}); | ||||||
| 			// refresh the identified users list | 			// refresh the identified users list | ||||||
|       await this.current_picture.load_identifications({ force_reload: true }) | 			await this.current_picture.load_identifications({ force_reload: true }); | ||||||
|       this.selector.empty().trigger('change') | 			this.selector.empty().trigger("change"); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| @@ -247,24 +245,24 @@ document.addEventListener('alpine:init', () => { | |||||||
| 		 * @param {PictureIdentification} identification | 		 * @param {PictureIdentification} identification | ||||||
| 		 * @return {boolean} | 		 * @return {boolean} | ||||||
| 		 */ | 		 */ | ||||||
|     can_be_removed (identification) { | 		can_be_removed(identification) { | ||||||
|       return user_is_sas_admin || identification.user.id === user_id | 			return user_is_sas_admin || identification.user.id === user_id; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Untag a user from the current picture | 		 * Untag a user from the current picture | ||||||
| 		 * @param {PictureIdentification} identification | 		 * @param {PictureIdentification} identification | ||||||
| 		 */ | 		 */ | ||||||
|     async remove_identification (identification) { | 		async remove_identification(identification) { | ||||||
| 			const res = await fetch(`/api/sas/relation/${identification.id}`, { | 			const res = await fetch(`/api/sas/relation/${identification.id}`, { | ||||||
|         method: 'DELETE' | 				method: "DELETE", | ||||||
|       }) | 			}); | ||||||
| 			if (res.ok && Array.isArray(this.current_picture.identifications)) { | 			if (res.ok && Array.isArray(this.current_picture.identifications)) { | ||||||
| 				this.current_picture.identifications = | 				this.current_picture.identifications = | ||||||
| 					this.current_picture.identifications.filter( | 					this.current_picture.identifications.filter( | ||||||
|             (i) => i.id !== identification.id | 						(i) => i.id !== identification.id, | ||||||
|           ) | 					); | ||||||
| 			} | 			} | ||||||
|     } | 		}, | ||||||
|   })) | 	})); | ||||||
| }) | }); | ||||||
|   | |||||||
| @@ -1,88 +1,85 @@ | |||||||
| const glob = require('glob') | const glob = require("glob"); | ||||||
| const path = require('path') | const path = require("node:path"); | ||||||
| const MiniCssExtractPlugin = require('mini-css-extract-plugin') | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); | ||||||
| const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') | const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); | ||||||
| const TerserPlugin = require('terser-webpack-plugin') | const TerserPlugin = require("terser-webpack-plugin"); | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   entry: glob.sync('./!(static)/static/webpack/**?(-)index.js').reduce((obj, el) => { | 	entry: glob | ||||||
|     obj[path.parse(el).name] = './' + el | 		.sync("./!(static)/static/webpack/**?(-)index.js") | ||||||
|     return obj | 		.reduce((obj, el) => { | ||||||
|  | 			obj[path.parse(el).name] = `./${el}`; | ||||||
|  | 			return obj; | ||||||
| 		}, {}), | 		}, {}), | ||||||
| 	output: { | 	output: { | ||||||
|     filename: '[name].js', | 		filename: "[name].js", | ||||||
|     path: path.resolve(__dirname, './staticfiles/generated/webpack'), | 		path: path.resolve(__dirname, "./staticfiles/generated/webpack"), | ||||||
|     clean: true | 		clean: true, | ||||||
| 	}, | 	}, | ||||||
|   plugins: [ | 	plugins: [new MiniCssExtractPlugin()], | ||||||
|     new MiniCssExtractPlugin() |  | ||||||
|   ], |  | ||||||
| 	optimization: { | 	optimization: { | ||||||
| 		minimizer: [ | 		minimizer: [ | ||||||
|       '...', | 			"...", | ||||||
| 			new CssMinimizerPlugin({ | 			new CssMinimizerPlugin({ | ||||||
|         parallel: true | 				parallel: true, | ||||||
| 			}), | 			}), | ||||||
| 			new TerserPlugin({ | 			new TerserPlugin({ | ||||||
| 				parallel: true, | 				parallel: true, | ||||||
| 				terserOptions: { | 				terserOptions: { | ||||||
| 					mangle: true, | 					mangle: true, | ||||||
| 					compress: { | 					compress: { | ||||||
|             drop_console: true | 						drop_console: true, | ||||||
|           } | 					}, | ||||||
|         } | 				}, | ||||||
|       }) | 			}), | ||||||
|     ] | 		], | ||||||
| 	}, | 	}, | ||||||
| 	module: { | 	module: { | ||||||
| 		rules: [ | 		rules: [ | ||||||
| 			{ | 			{ | ||||||
| 				test: /\.css$/, | 				test: /\.css$/, | ||||||
| 				sideEffects: true, | 				sideEffects: true, | ||||||
|         use: [ | 				use: [MiniCssExtractPlugin.loader, "css-loader"], | ||||||
|           MiniCssExtractPlugin.loader, |  | ||||||
|           'css-loader' |  | ||||||
|         ] |  | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				test: /\.(jpe?g|png|gif)$/i, | 				test: /\.(jpe?g|png|gif)$/i, | ||||||
|         type: 'asset/resource' | 				type: "asset/resource", | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				test: /\.m?js$/, | 				test: /\.m?js$/, | ||||||
| 				exclude: /node_modules/, | 				exclude: /node_modules/, | ||||||
| 				use: { | 				use: { | ||||||
|           loader: 'babel-loader', | 					loader: "babel-loader", | ||||||
| 					options: { | 					options: { | ||||||
|             presets: ['@babel/preset-env'] | 						presets: ["@babel/preset-env"], | ||||||
|           } | 					}, | ||||||
|         } | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				test: /\.js$/, | 				test: /\.js$/, | ||||||
|         enforce: 'pre', | 				enforce: "pre", | ||||||
|         use: ['source-map-loader'] | 				use: ["source-map-loader"], | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
|         test: require.resolve('jquery'), | 				test: require.resolve("jquery"), | ||||||
|         loader: 'expose-loader', | 				loader: "expose-loader", | ||||||
| 				options: { | 				options: { | ||||||
| 					exposes: [ | 					exposes: [ | ||||||
| 						{ | 						{ | ||||||
|               globalName: ['$'], | 							globalName: ["$"], | ||||||
|               override: true | 							override: true, | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
|               globalName: ['jQuery'], | 							globalName: ["jQuery"], | ||||||
|               override: true | 							override: true, | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
|               globalName: ['window.jQuery'], | 							globalName: ["window.jQuery"], | ||||||
|               override: true | 							override: true, | ||||||
|             } | 						}, | ||||||
|           ] | 					], | ||||||
|         } | 				}, | ||||||
|       } | 			}, | ||||||
|     ] | 		], | ||||||
|   } | 	}, | ||||||
| } | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user