mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 17:13:08 +00:00 
			
		
		
		
	Apply standard formater and linter on js files
This commit is contained in:
		| @@ -8,6 +8,10 @@ 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 | ||||||
|  |     rev: v17.1.2 | ||||||
|  |     hooks: | ||||||
|  |       - id: standard | ||||||
|   - repo: https://github.com/rtts/djhtml |   - repo: https://github.com/rtts/djhtml | ||||||
|     rev: 3.0.6 |     rev: 3.0.6 | ||||||
|     hooks: |     hooks: | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| [](#) | [](#) | ||||||
| [](https://ae-utbm.github.io/sith) | [](https://ae-utbm.github.io/sith) | ||||||
| [](https://squidfunk.github.io/mkdocs-material/) | [](https://squidfunk.github.io/mkdocs-material/) | ||||||
|  | [](https://standardjs.com) | ||||||
| [](https://discord.gg/xk9wfpsufm) | [](https://discord.gg/xk9wfpsufm) | ||||||
|  |  | ||||||
| ### This is the source code of the UTBM's student association available at [https://ae.utbm.fr/](https://ae.utbm.fr/). | ### This is the source code of the UTBM's student association available at [https://ae.utbm.fr/](https://ae.utbm.fr/). | ||||||
|   | |||||||
| @@ -1,24 +1,20 @@ | |||||||
| $(document).ready(function () { | $(document).ready(function () { | ||||||
|  |   $('#poster_list #view').click(function (e) { | ||||||
|  |     $('#view').removeClass('active') | ||||||
|  |   }) | ||||||
|  |  | ||||||
|     $("#poster_list #view").click(function(e){ |   $('#poster_list .poster .image').click(function (e) { | ||||||
|         $("#view").removeClass("active"); |     let el = $(e.target) | ||||||
|     }); |     if (el.hasClass('image')) { el = el.find('img') } | ||||||
|  |     $('#poster_list #view #placeholder').html(el.clone()) | ||||||
|  |  | ||||||
|     $("#poster_list .poster .image").click(function(e){ |     $('#view').addClass('active') | ||||||
|  |   }) | ||||||
|         el = $(e.target); |  | ||||||
|         if(el.hasClass("image")) |  | ||||||
|             el = el.find("img") |  | ||||||
|         $("#poster_list #view #placeholder").html(el.clone()); |  | ||||||
|  |  | ||||||
|         $("#view").addClass("active"); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|   $(document).keyup(function (e) { |   $(document).keyup(function (e) { | ||||||
|         if (e.keyCode == 27) { // escape key maps to keycode `27` |     if (e.keyCode === 27) { // escape key maps to keycode `27` | ||||||
|             e.preventDefault(); |       e.preventDefault() | ||||||
|             $("#view").removeClass("active"); |       $('#view').removeClass('active') | ||||||
|     } |     } | ||||||
|     }); |   }) | ||||||
|  | }) | ||||||
| }); |  | ||||||
|   | |||||||
| @@ -1,118 +1,104 @@ | |||||||
|  | // TODO: remove disable | ||||||
|  | /* eslint-disable no-undef, camelcase */ | ||||||
| $(document).ready(function () { | $(document).ready(function () { | ||||||
|  |   transition_time = 1000 | ||||||
|  |  | ||||||
|     transition_time = 1000; |   i = 0 | ||||||
|  |   max = $('#slideshow .slide').length | ||||||
|     i = 0; |  | ||||||
|     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').removeClass('progress') | ||||||
|         $("#slideshow #progress_bar").css("transition", "none"); |     $('#slideshow #progress_bar').addClass('init') | ||||||
|         $("#slideshow #progress_bar").removeClass("progress"); |  | ||||||
|         $("#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').addClass('progress') | ||||||
|         $("#slideshow #progress_bar").removeClass("init"); |     $('#slideshow #progress_bar').css('transition', 'width ' + display_time + 's linear') | ||||||
|         $("#slideshow #progress_bar").addClass("progress"); |  | ||||||
|         $("#slideshow #progress_bar").css("transition", "width " + display_time + "s linear") |  | ||||||
|  |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|     function next() |   function next () { | ||||||
|     { |     init_progress_bar() | ||||||
|  |     slide = $($('#slideshow .slide').get(i % max)) | ||||||
|  |     slide.removeClass('center') | ||||||
|  |     slide.addClass('left') | ||||||
|  |  | ||||||
|         init_progress_bar(); |     next_slide = $($('#slideshow .slide').get((i + 1) % max)) | ||||||
|         slide = $($("#slideshow .slide").get(i % max)); |     next_slide.removeClass('right') | ||||||
|         slide.removeClass("center"); |     next_slide.addClass('center') | ||||||
|         slide.addClass("left"); |     display_time = next_slide.attr('display_time') || 2 | ||||||
|  |  | ||||||
|         next_slide = $($("#slideshow .slide").get((i + 1) % max)); |     $('#slideshow .bullet').removeClass('active') | ||||||
|         next_slide.removeClass("right"); |     bullet = $('#slideshow .bullet')[(i + 1) % max] | ||||||
|         next_slide.addClass("center"); |     $(bullet).addClass('active') | ||||||
|         display_time = next_slide.attr("display_time") || 2; |  | ||||||
|  |  | ||||||
|         $("#slideshow .bullet").removeClass("active"); |     i = (i + 1) % max | ||||||
|         bullet = $("#slideshow .bullet")[(i + 1) % max]; |  | ||||||
|         $(bullet).addClass("active"); |  | ||||||
|  |  | ||||||
|         i = (i + 1) % max; |  | ||||||
|  |  | ||||||
|     setTimeout(function () { |     setTimeout(function () { | ||||||
|  |       others_left = $('#slideshow .slide.left') | ||||||
|  |       others_left.removeClass('left') | ||||||
|  |       others_left.addClass('right') | ||||||
|  |  | ||||||
|             others_left = $("#slideshow .slide.left"); |       start_progress_bar(display_time) | ||||||
|             others_left.removeClass("left"); |       next_trigger = setTimeout(next, display_time * 1000) | ||||||
|             others_left.addClass("right"); |     }, transition_time) | ||||||
|  |  | ||||||
|             start_progress_bar(display_time); |  | ||||||
|             next_trigger = setTimeout(next, display_time * 1000); |  | ||||||
|  |  | ||||||
|         }, 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(function () { | ||||||
|     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(function(e){ |     if (!$('#slideshow').hasClass('fullscreen')) { | ||||||
|         if(!$("#slideshow").hasClass("fullscreen")) |       console.log('Entering fullscreen ...') | ||||||
|         { |       enterFullscreen() | ||||||
|             console.log("Entering fullscreen ..."); |  | ||||||
|             enterFullscreen(); |  | ||||||
|     } else { |     } else { | ||||||
|             console.log("Exiting fullscreen ..."); |       console.log('Exiting fullscreen ...') | ||||||
|             exitFullscreen(); |       exitFullscreen() | ||||||
|     } |     } | ||||||
|     }); |   }) | ||||||
|  |  | ||||||
|   $(document).keyup(function (e) { |   $(document).keyup(function (e) { | ||||||
|         if (e.keyCode == 27) { // escape key maps to keycode `27` |     if (e.keyCode === 27) { // escape key maps to keycode `27` | ||||||
|             e.preventDefault(); |       e.preventDefault() | ||||||
|             console.log("Exiting fullscreen ..."); |       console.log('Exiting fullscreen ...') | ||||||
|             exitFullscreen(); |       exitFullscreen() | ||||||
|     } |     } | ||||||
|     }); |   }) | ||||||
|  | }) | ||||||
| }); |  | ||||||
|   | |||||||
| @@ -1,59 +1,60 @@ | |||||||
|  | /* eslint-disable camelcase */ | ||||||
| $(function () { | $(function () { | ||||||
|     buttons = $(".choose_file_button"); |   // const buttons = $('.choose_file_button') | ||||||
|     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: function (event) { | ||||||
|             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('value', $('#file_id').attr('value')) | ||||||
|                 $( this ).dialog( "close" ); |           $(this).dialog('close') | ||||||
|         }, |         }, | ||||||
|             disabled: true, |         disabled: true | ||||||
|       } |       } | ||||||
|         ], |     ] | ||||||
|     }); |   }) | ||||||
|     $( ".choose_file_button" ).button().on( "click", function() { |   $('.choose_file_button').button().on('click', function () { | ||||||
|         popup = popups.filter("[name="+$(this).attr('name')+"]"); |     const popup = popups.filter('[name=' + $(this).attr('name') + ']') | ||||||
|         console.log(popup); |     console.log(popup) | ||||||
|         popup.html('<iframe src="/file/popup" width="100%" height="95%"></iframe><div id="file_id" value="null" />'); |     popup.html('<iframe src="/file/popup" width="100%" height="95%"></iframe><div id="file_id" value="null" />') | ||||||
|         popup.dialog({title: $(this).text()}).dialog( "open" ); |     popup.dialog({ title: $(this).text() }).dialog('open') | ||||||
|     }); |   }) | ||||||
|     $("#quick_notif li").click(function () { |   $('#quick_notif li').click(function () { | ||||||
|         $(this).hide(); |     $(this).hide() | ||||||
|  |   }) | ||||||
| }) | }) | ||||||
| }); |  | ||||||
|  |  | ||||||
| function createQuickNotif(msg) { | function createQuickNotif (msg) { // eslint-disable-line no-unused-vars | ||||||
|   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) | ||||||
| } | } | ||||||
|  |  | ||||||
| function deleteQuickNotifs() { | function deleteQuickNotifs () { // eslint-disable-line no-unused-vars | ||||||
|   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) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| function display_notif() { | function display_notif () { // eslint-disable-line no-unused-vars | ||||||
|     $('#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 | ||||||
| @@ -62,12 +63,11 @@ 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 | ||||||
| function getCSRFToken() { | function getCSRFToken () { // eslint-disable-line no-unused-vars | ||||||
|     return $("[name=csrfmiddlewaretoken]").val(); |   return $('[name=csrfmiddlewaretoken]').val() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const initialUrlParams = new URLSearchParams(window.location.search) // eslint-disable-line no-unused-vars | ||||||
| const initialUrlParams = new URLSearchParams(window.location.search); |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @readonly |  * @readonly | ||||||
| @@ -76,8 +76,8 @@ 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 +85,27 @@ const History = { | |||||||
|  * @param {History} action |  * @param {History} action | ||||||
|  * @param {URL | null} url |  * @param {URL | null} url | ||||||
|  */ |  */ | ||||||
| function update_query_string(key, value, action = History.REPLACE, url = null) { | function update_query_string (key, value, action = History.REPLACE, url = null) { // eslint-disable-line no-unused-vars | ||||||
|   if (!url) { |   if (!url) { | ||||||
|         url = new URL(window.location.href); |     url = 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) |     url.searchParams.delete(key) | ||||||
|   } else if (Array.isArray(value)) { |   } else if (Array.isArray(value)) { | ||||||
|     url.searchParams.delete(key) |     url.searchParams.delete(key) | ||||||
|     value.forEach((v) => url.searchParams.append(key, v)) |     value.forEach((v) => url.searchParams.append(key, v)) | ||||||
|   } else { |   } else { | ||||||
|         url.searchParams.set(key, value); |     url.searchParams.set(key, value) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (action === History.PUSH) { |   if (action === History.PUSH) { | ||||||
|         history.pushState(null, "", url.toString()); |     window.history.pushState(null, '', url.toString()) | ||||||
|   } else if (action === History.REPLACE) { |   } else if (action === History.REPLACE) { | ||||||
|         history.replaceState(null, "", url.toString()); |     window.history.replaceState(null, '', url.toString()) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|     return url; |   return url | ||||||
| } | } | ||||||
|  |  | ||||||
| // 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 +116,27 @@ function update_query_string(key, value, action = History.REPLACE, url = null) { | |||||||
|  * @param {string} url The paginated endpoint to fetch |  * @param {string} url The paginated endpoint to fetch | ||||||
|  * @return {Promise<Object[]>} |  * @return {Promise<Object[]>} | ||||||
|  */ |  */ | ||||||
| async function fetch_paginated(url) { | async function fetch_paginated (url) { // eslint-disable-line no-unused-vars | ||||||
|   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') | ||||||
|  |  | ||||||
|   let first_page = (await ( await fetch(paginated_url)).json()); |   const first_page = (await (await fetch(paginated_url)).json()) | ||||||
|   let 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) { | ||||||
|       let 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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | /* eslint-disable camelcase */ | ||||||
| /** | /** | ||||||
|  * Builders to use Select2 in our templates. |  * Builders to use Select2 in our templates. | ||||||
|  * |  * | ||||||
| @@ -157,15 +158,15 @@ | |||||||
| /** | /** | ||||||
|  * @param {Select2Options} options |  * @param {Select2Options} options | ||||||
|  */ |  */ | ||||||
| function sithSelect2(options) { | function sithSelect2 (options) { // eslint-disable-line no-unused-vars | ||||||
|   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 || {}) | ||||||
|   }); |   }) | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -179,12 +180,12 @@ 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 | ||||||
|  */ |  */ | ||||||
| function local_data_source(source, options) { | function local_data_source (source, options) { // eslint-disable-line no-unused-vars | ||||||
|   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 } | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -202,11 +203,11 @@ 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 | ||||||
|  */ |  */ | ||||||
| function remote_data_source(source, options) { | function remote_data_source (source, options) { // eslint-disable-line no-unused-vars | ||||||
|   jQuery.ajaxSettings.traditional = true; |   jQuery.ajaxSettings.traditional = true | ||||||
|   let 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) { | ||||||
| @@ -214,25 +215,25 @@ function remote_data_source(source, options) { | |||||||
|         search: params.term, |         search: params.term, | ||||||
|         exclude: [ |         exclude: [ | ||||||
|           ...(this.val() || []).map((i) => parseInt(i)), |           ...(this.val() || []).map((i) => parseInt(i)), | ||||||
|           ...(options.excluded ? options.excluded() : []), |           ...(options.excluded ? options.excluded() : []) | ||||||
|         ], |         ] | ||||||
|       }; |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
|   if (!!options.result_converter) { |  | ||||||
|     params["processResults"] = function (data) { |  | ||||||
|       return { results: data.results.map(options.result_converter) }; |  | ||||||
|     }; |  | ||||||
|       } |       } | ||||||
|   if (!!options.overrides) { |  | ||||||
|     Object.assign(params, options.overrides); |  | ||||||
|     } |     } | ||||||
|   return { ajax: params }; |   } | ||||||
|  |   if (options.result_converter) { | ||||||
|  |     params.processResults = function (data) { | ||||||
|  |       return { results: data.results.map(options.result_converter) } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (options.overrides) { | ||||||
|  |     Object.assign(params, options.overrides) | ||||||
|  |   } | ||||||
|  |   return { ajax: params } | ||||||
| } | } | ||||||
|  |  | ||||||
| function item_formatter(user) { | function item_formatter (user) { // eslint-disable-line no-unused-vars | ||||||
|   if (user.loading) { |   if (user.loading) { | ||||||
|     return user.text; |     return user.text | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -244,18 +245,18 @@ function item_formatter(user) { | |||||||
| 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,167 +1,171 @@ | |||||||
|  | /* eslint-disable camelcase */ | ||||||
|  | /* global cytoscape, initialUrlParams, History, update_query_string */ | ||||||
|  |  | ||||||
| async function get_graph_data (url, godfathers_depth, godchildren_depth) { | async function get_graph_data (url, godfathers_depth, godchildren_depth) { | ||||||
|   let 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) { | ||||||
|   let cy = cytoscape({ |   const cy = cytoscape({ | ||||||
|     boxSelectionEnabled: false, |     boxSelectionEnabled: false, | ||||||
|     autounselectify: true, |     autounselectify: true, | ||||||
|  |  | ||||||
|     container: container, |     container, | ||||||
|     elements: data, |     elements: data, | ||||||
|     minZoom: 0.5, |     minZoom: 0.5, | ||||||
|  |  | ||||||
|     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 | ||||||
|       }, |       } | ||||||
|     }, |     } | ||||||
|   }); |   }) | ||||||
|   let 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 */ | ||||||
|   let 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') | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
|     }); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   let 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() |     cy.elements() | ||||||
|       .aStar({ |       .aStar({ | ||||||
|         root: el, |         root: el, | ||||||
|         goal: active_user, |         goal: active_user | ||||||
|       }) |       }) | ||||||
|       .path.forEach((el) => { |       .path.forEach((el) => { | ||||||
|         el.removeClass("not-traversed"); |         el.removeClass('not-traversed') | ||||||
|         el.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: function (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: function (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: function (el) { | ||||||
|           reset_graph(); |           reset_graph() | ||||||
|         }, |         } | ||||||
|       }, |       } | ||||||
|     ], |     ] | ||||||
|   }); |   }) | ||||||
|  |  | ||||||
|   return cy; |   return cy | ||||||
| } | } | ||||||
|  |  | ||||||
| document.addEventListener("alpine:init", () => { | /* global api_url, active_user, depth_min, depth_max */ | ||||||
|  | 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 | ||||||
|  |  | ||||||
| @@ -170,104 +174,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) { | ||||||
|     let value = parseInt(initialUrlParams.get(prop)); |     const value = parseInt(initialUrlParams.get(prop)) | ||||||
|     if (isNaN(value) || value < depth_min || value > depth_max) { |     if (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 () { | ||||||
|       let 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) => { |       ['godfathers_depth', 'godchildren_depth'].forEach((param) => { | ||||||
|         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(new Object({ 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,7 +1,9 @@ | |||||||
| function alpine_webcam_builder( | /* eslint-disable camelcase */ | ||||||
|  | /* global DataTransfer */ | ||||||
|  | function alpine_webcam_builder ( // eslint-disable-line no-unused-vars | ||||||
|   default_picture, |   default_picture, | ||||||
|   delete_url, |   delete_url, | ||||||
|   can_delete_picture, |   can_delete_picture | ||||||
| ) { | ) { | ||||||
|   return () => ({ |   return () => ({ | ||||||
|     can_edit_picture: false, |     can_edit_picture: false, | ||||||
| @@ -14,97 +16,98 @@ function alpine_webcam_builder( | |||||||
|     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) => { | ||||||
|           let 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) { | ||||||
|         let 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) | ||||||
|  |         }) | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     take_picture () { |     take_picture () { | ||||||
|       let 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 */ | ||||||
|       let 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) => { |       this.video.srcObject.getTracks().forEach((track) => { | ||||||
|         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']) | ||||||
|         let file = new File([blob], filename, { |         const file = new File([blob], filename, { | ||||||
|           type: "image/webp", |           type: 'image/webp' | ||||||
|         }); |         }) | ||||||
|  |  | ||||||
|         let 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 | ||||||
|  |  | ||||||
| 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 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| require("@fortawesome/fontawesome-free/css/all.css"); | require('@fortawesome/fontawesome-free/css/all.css') | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								core/static/webpack/jquery-index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								core/static/webpack/jquery-index.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,17 +1,17 @@ | |||||||
| 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 | ||||||
| @@ -19,7 +19,7 @@ require("jquery-ui/themes/base/all.css"); | |||||||
|  * @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) { | export function shorten (selector, options) { | ||||||
|   $(selector).shorten(options); |   $(selector).shorten(options) | ||||||
| } | } | ||||||
|  |  | ||||||
| window.shorten = shorten; | window.shorten = shorten | ||||||
|   | |||||||
| @@ -1,77 +1,79 @@ | |||||||
|  | /* eslint-disable camelcase */ | ||||||
|  | /* global basket, click_api_url, csrf_token, products_autocomplete */ | ||||||
| document.addEventListener('alpine:init', () => { | document.addEventListener('alpine:init', () => { | ||||||
|   Alpine.data('counter', () => ({ |   Alpine.data('counter', () => ({ | ||||||
|         basket: 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((acc, cur) => acc + cur["qty"] * cur["price"], 0); |         .reduce((acc, cur) => acc + cur.qty * cur.price, 0) | ||||||
|             return total / 100; |       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() | ||||||
|             let 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 () { | $(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: function (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: function (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: function (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($.grep(products_autocomplete, function (value) { | ||||||
|                 value = value.tags; |         value = value.tags | ||||||
|                 return matcher.test( value ); |         return matcher.test(value) | ||||||
|             })); |       })) | ||||||
|         }, |     } | ||||||
|     }); |   }) | ||||||
|  |  | ||||||
|   /* 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() | ||||||
| }); | }) | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | /* 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 | ||||||
| @@ -6,7 +7,7 @@ | |||||||
|  * @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 | ||||||
| @@ -14,14 +15,14 @@ const BASKET_ITEMS_COOKIE_NAME = "basket_items"; | |||||||
|  * @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 | ||||||
|  |  | ||||||
|     let 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]) | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -29,17 +30,17 @@ function getCookie(name) { | |||||||
|  * @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)) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -53,7 +54,7 @@ document.addEventListener('alpine:init', () => { | |||||||
|          */ |          */ | ||||||
|     get_total () { |     get_total () { | ||||||
|       return this.items |       return this.items | ||||||
|                 .reduce((acc, item) => acc + item["quantity"] * item["unit_price"], 0); |         .reduce((acc, item) => acc + item.quantity * item.unit_price, 0) | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -61,8 +62,8 @@ document.addEventListener('alpine:init', () => { | |||||||
|          * @param {BasketItem} item |          * @param {BasketItem} item | ||||||
|          */ |          */ | ||||||
|     add (item) { |     add (item) { | ||||||
|             item.quantity++; |       item.quantity++ | ||||||
|             this.set_cookies(); |       this.set_cookies() | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -70,23 +71,23 @@ document.addEventListener('alpine:init', () => { | |||||||
|          * @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() | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -95,9 +96,9 @@ document.addEventListener('alpine:init', () => { | |||||||
|          */ |          */ | ||||||
|     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,17 +110,17 @@ document.addEventListener('alpine:init', () => { | |||||||
|          * @returns {BasketItem} The created item |          * @returns {BasketItem} The created item | ||||||
|          */ |          */ | ||||||
|     create_item (id, name, price) { |     create_item (id, name, price) { | ||||||
|             let new_item = { |       const new_item = { | ||||||
|                 id: id, |         id, | ||||||
|                 name: 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 | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -135,10 +136,10 @@ document.addEventListener('alpine:init', () => { | |||||||
|       // 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,3 +1,6 @@ | |||||||
|  | /* global et_data, et_data_url, billing_info_url, | ||||||
|  |   billing_info_success_message, billing_info_failure_message */ | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @readonly |  * @readonly | ||||||
|  * @enum {number} |  * @enum {number} | ||||||
| @@ -5,75 +8,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 | ||||||
|             let 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.map((err) => err.loc).flat() | ||||||
|                 Array.from(form.querySelectorAll("input")) |         Array.from(form.querySelectorAll('input')) | ||||||
|           .filter((elem) => errors.includes(elem.name)) |           .filter((elem) => errors.includes(elem.name)) | ||||||
|           .forEach((elem) => { |           .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 '' | ||||||
|         }, |     } | ||||||
|     })); |   })) | ||||||
| }); | }) | ||||||
|   | |||||||
							
								
								
									
										3138
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3138
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -14,18 +14,26 @@ | |||||||
|     "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", | ||||||
|         "babel-loader": "^9.2.1", |         "babel-loader": "^9.2.1", | ||||||
|  |         "css-loader": "^7.1.2", | ||||||
|  |         "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" | ||||||
|         "css-loader": "^7.1.2", |  | ||||||
|         "css-minimizer-webpack-plugin": "^7.0.0" |  | ||||||
|     }, |     }, | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@fortawesome/fontawesome-free": "^6.6.0", |         "@fortawesome/fontawesome-free": "^6.6.0", | ||||||
|   | |||||||
| @@ -1,3 +1,7 @@ | |||||||
|  | /* 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 | ||||||
| @@ -9,21 +13,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) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
| @@ -34,17 +39,17 @@ class PictureWithIdentifications { | |||||||
|    */ |    */ | ||||||
|   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 | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
| @@ -52,20 +57,20 @@ class PictureWithIdentifications { | |||||||
|    * @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[] | ||||||
| @@ -80,14 +85,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 | ||||||
| @@ -110,7 +115,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 | ||||||
| @@ -120,40 +125,40 @@ document.addEventListener("alpine:init", () => { | |||||||
|  |  | ||||||
|     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) { /* Avoid recursive updates */ | ||||||
|           return; |           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 === 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() | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -167,74 +172,74 @@ document.addEventListener("alpine:init", () => { | |||||||
|     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) { |  | ||||||
|         this.moderation_error = `${gettext("Couldn't moderate picture")} : ${res.statusText}`; |  | ||||||
|         return; |  | ||||||
|         } |         } | ||||||
|       this.current_picture.is_moderated = true; |       ) | ||||||
|       this.current_picture.asked_for_removal = false; |       if (!res.ok) { | ||||||
|  |         this.moderation_error = `${gettext("Couldn't moderate picture")} : ${res.statusText}` | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.current_picture.is_moderated = true | ||||||
|  |       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) => 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') | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -243,7 +248,7 @@ document.addEventListener("alpine:init", () => { | |||||||
|      * @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 | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -252,14 +257,14 @@ document.addEventListener("alpine:init", () => { | |||||||
|      */ |      */ | ||||||
|     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,14 +1,13 @@ | |||||||
| const glob = require('glob'); | const glob = require('glob') | ||||||
| const path = require('path'); | const path = require('path') | ||||||
| const webpack = require("webpack"); | 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.sync('./!(static)/static/webpack/**?(-)index.js').reduce((obj, el) => { | ||||||
|     obj[path.parse(el).name] = './' + el; |     obj[path.parse(el).name] = './' + el | ||||||
|     return obj; |     return obj | ||||||
|   }, {}), |   }, {}), | ||||||
|   output: { |   output: { | ||||||
|     filename: '[name].js', |     filename: '[name].js', | ||||||
| @@ -16,24 +15,24 @@ module.exports = { | |||||||
|     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: [ | ||||||
| @@ -42,18 +41,18 @@ module.exports = { | |||||||
|         sideEffects: true, |         sideEffects: true, | ||||||
|         use: [ |         use: [ | ||||||
|           MiniCssExtractPlugin.loader, |           MiniCssExtractPlugin.loader, | ||||||
|           "css-loader", |           'css-loader' | ||||||
|         ], |         ] | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|otf)$/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'] | ||||||
|           } |           } | ||||||
| @@ -61,12 +60,12 @@ module.exports = { | |||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         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: [ | ||||||
|             { |             { | ||||||
| @@ -81,9 +80,9 @@ module.exports = { | |||||||
|               globalName: ['window.jQuery'], |               globalName: ['window.jQuery'], | ||||||
|               override: true |               override: true | ||||||
|             } |             } | ||||||
|           ], |           ] | ||||||
|         }, |         } | ||||||
|       }, |       } | ||||||
|     ], |     ] | ||||||
|   }, |   } | ||||||
| }; | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user