|
|
@@ -1,4 +1,13 @@
|
|
|
|
|
|
+const variants_uris = {
|
|
|
+ "preprod.opentalent.fr": "https://api.preprod.opentalent.fr",
|
|
|
+ "local.sub.opentalent.fr": "https://local.api.opentalent.fr",
|
|
|
+ "typo3": "http://docker.nginx.opentalent.fr"
|
|
|
+};
|
|
|
+
|
|
|
+const base_uri = variants_uris[$(location).attr('hostname')];
|
|
|
+let apiGetUrl = base_uri + '/api/public/federation_structures?_format=json&page=1&itemsPerPage=99999';
|
|
|
+
|
|
|
let structures_categories = {
|
|
|
'1MC': 'Musique',
|
|
|
'2TH': 'Théatre',
|
|
|
@@ -11,106 +20,208 @@ let structures_categories = {
|
|
|
'OTAU': 'Autres'
|
|
|
}
|
|
|
|
|
|
+// Converts numeric degrees to radians
|
|
|
+function toRad(Value)
|
|
|
+{
|
|
|
+ return Value * Math.PI / 180;
|
|
|
+}
|
|
|
+
|
|
|
+//This function takes in latitude and longitude of two location and returns the distance between them as the crow flies (in km)
|
|
|
+function sphericDistance(lat1, lon1, lat2, lon2)
|
|
|
+{
|
|
|
+ let R = 6371; // km
|
|
|
+ let dLat = toRad(lat2-lat1);
|
|
|
+ let dLon = toRad(lon2-lon1);
|
|
|
+ lat1 = toRad(lat1);
|
|
|
+ lat2 = toRad(lat2);
|
|
|
+
|
|
|
+ let a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
|
+ Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
|
|
|
+ let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
|
+ return R * c;
|
|
|
+}
|
|
|
+
|
|
|
// Specific JS used for the Structures layout
|
|
|
// > Needs to be loaded after the main.js script
|
|
|
$(document).ready(function() {
|
|
|
|
|
|
- // #### Load the structures data
|
|
|
- let structures = $('.ot-structures-frame').data('structures');
|
|
|
- console.log(structures);
|
|
|
+ // Init
|
|
|
+ let structureFrame = $('.ot-structures-frame').first();
|
|
|
+
|
|
|
+ let organizationId = structureFrame.data('org-id');
|
|
|
+
|
|
|
+ let resultsPageDiv = structureFrame.find('.structures-page').first();
|
|
|
+ let pleaseWaitSpan = structureFrame.find('.please-wait').first();
|
|
|
+ let noResultSpan = structureFrame.find('.no-result').first();
|
|
|
+ let cardDivModel = structureFrame.find('.structure-card-model').first();
|
|
|
+ let paginationBar = structureFrame.find('.pagination-bar').first();
|
|
|
+ let paginationList = structureFrame.find('.pagination-list').first();
|
|
|
+ let gotoFirstPage = structureFrame.find('.goto-first-page').first();
|
|
|
+ let gotoLastPage = structureFrame.find('.goto-last-page').first();
|
|
|
+
|
|
|
+ let form = $("#structure-search-form");
|
|
|
+ let whatInput = form.find("input[name='search-query']").first();
|
|
|
+ let cityInput = form.find("input[name='search-city']").first();
|
|
|
+ let latInput = form.find("input[name='lat']").first();
|
|
|
+ let longInput = form.find("input[name='long']").first();
|
|
|
+ let categorySelect = form.find("select[name='search-category']").first();
|
|
|
+ let provinceSelect = form.find("select[name='search-province']").first();
|
|
|
+ let federationSelect = form.find("select[name='search-federation']").first();
|
|
|
+ let radiusSelect = form.find("select[name='search-radius']").first();
|
|
|
+
|
|
|
+ // #### Instanciate and populate leaflet map
|
|
|
+ let mapDiv = $('#structure-map').first();
|
|
|
+ let mapId = $(mapDiv).attr("id");
|
|
|
+
|
|
|
+ // Instanciate the map object @see https://leafletjs.com/reference-1.6.0.html#map-factory
|
|
|
+ const mapOptions = {scrollWheelZoom: false, zoomSnap: 0.25};
|
|
|
+ map = L.map(mapId, mapOptions);
|
|
|
+ map.setView([46.71, 2.14], 6);
|
|
|
+
|
|
|
+ // Add the tile layer
|
|
|
+ L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
|
|
|
+ attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
|
|
+ }).addTo(map);
|
|
|
+
|
|
|
+ // Set the view
|
|
|
+ bounds = L.latLngBounds(
|
|
|
+ L.latLng(51.03, -5.78),
|
|
|
+ L.latLng(41.2, 9.70)
|
|
|
+ );
|
|
|
+ map.fitBounds(bounds);
|
|
|
+
|
|
|
+ var clusters = null;
|
|
|
+
|
|
|
+
|
|
|
+ // #### Filters
|
|
|
+
|
|
|
+ // The current query
|
|
|
+ var query = {};
|
|
|
+
|
|
|
+ // Update the current query with form data
|
|
|
+ let updateQuery = function() {
|
|
|
+ query['what'] = whatInput.val();
|
|
|
+ query['lat'] = latInput.val();
|
|
|
+ query['long'] = longInput.val();
|
|
|
+ query['category'] = categorySelect.val();
|
|
|
+ query['province'] = provinceSelect.val();
|
|
|
+ query['federation'] = federationSelect.val();
|
|
|
+ query['radius'] = radiusSelect.val();
|
|
|
+ }
|
|
|
|
|
|
+ // Does the given structure match the current query
|
|
|
+ let matchCurrentQuery = function(structure) {
|
|
|
|
|
|
- // Display map on network structures page
|
|
|
- if ($('.ot-structures-frame #structure-map').length) {
|
|
|
- // #### Instanciate and populate leaflet map
|
|
|
- let mapDiv = $('#structure-map').first();
|
|
|
- let mapId = $(mapDiv).attr("id");
|
|
|
- if (!mapId) {
|
|
|
- console.error('missing id attribute for map');
|
|
|
- return;
|
|
|
+ // Filter by name
|
|
|
+ if (query['what'] && !structure.name.toLowerCase().includes(query['what'].toLowerCase())) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- // Instanciate the map object @see https://leafletjs.com/reference-1.6.0.html#map-factory
|
|
|
- const mapOptions = {scrollWheelZoom: false, zoomSnap: 0.25};
|
|
|
- map = L.map(mapId, mapOptions);
|
|
|
- map.setView([46.71, 2.14], 6);
|
|
|
-
|
|
|
- // Add the tile layer
|
|
|
- L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
|
|
|
- attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
|
|
- }).addTo(map);
|
|
|
-
|
|
|
- // Set the view
|
|
|
- bounds = L.latLngBounds(
|
|
|
- L.latLng(51.03, -5.78),
|
|
|
- L.latLng(41.2, 9.70)
|
|
|
- );
|
|
|
- map.fitBounds(bounds);
|
|
|
|
|
|
- // Load clustered markers on leaflet map
|
|
|
- var clusters = L.markerClusterGroup();
|
|
|
+ // filter by geographical position
|
|
|
+ if (query['lat'] && query['long']) {
|
|
|
+ if (!structure.latitude || !structure.longitude) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- structures.forEach(function (item) {
|
|
|
- if (item.longitude && item.latitude) {
|
|
|
- let marker = L.marker([item.latitude, item.longitude]);
|
|
|
- marker.bindPopup(`<b>${item.name}</b><br/>${item.postalCode} ${item.addressCity}<br/><a href="${item.otherWebsite}" target="_blank">${item.otherWebsite}</a>`);
|
|
|
- clusters.addLayer(marker);
|
|
|
+ let radius = Number(query['radius']) ?? 0;
|
|
|
+
|
|
|
+ // radius is increased by 10km to approximate the city radius
|
|
|
+ radius += 10;
|
|
|
+
|
|
|
+ if (sphericDistance(query['lat'], query['long'], structure.latitude, structure.longitude) > radius) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- });
|
|
|
- map.addLayer(clusters);
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // #### Populate results list
|
|
|
- let resultsPageDiv = $('.ot-structures-frame .structures-page');
|
|
|
- let pleaseWaitSpan = $('.ot-structures-frame .please-wait');
|
|
|
- let noResultSpan = $('.ot-structures-frame .no-result');
|
|
|
- let cardDivModel = $('.ot-structures-frame .structure-card-model');
|
|
|
- let paginationBar = $('.ot-structures-frame .pagination-bar');
|
|
|
- let paginationList = $('.ot-structures-frame .pagination-list');
|
|
|
- let gotoFirstPage = $(".ot-structures-frame .goto-first-page")
|
|
|
- let gotoLastPage = $(".ot-structures-frame .goto-last-page")
|
|
|
+ // filter by category
|
|
|
+ if (query['category'] && !structure.categories.includes(query['category'])) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- let totalStructures = structures.length;
|
|
|
- let itemsPerPage = 10;
|
|
|
+ if (query['province']) {
|
|
|
+ let province = query['province'];
|
|
|
|
|
|
- pleaseWaitSpan.show();
|
|
|
+ // A leading '_' may have been added to prevent the '0' from being stripped by fluid
|
|
|
+ province = province.replace('_', '');
|
|
|
|
|
|
- if (totalStructures === 0) {
|
|
|
- noResultSpan.show();
|
|
|
+ if (!structure.postalCode.startsWith(province)) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (query['federation'] && structure.parentId !== query['federation']) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // structure match every filter
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ let populateFederationsSelect = function () {
|
|
|
+ federationSelect.children('option:not(:first)').remove();
|
|
|
+ let has_options = false;
|
|
|
+ structures.forEach(function (structure) {
|
|
|
+ if (structure.n1Id === organizationId && structure.principalType.endsWith('FEDERATION')) {
|
|
|
+ let id = structure['@id'].split('/').pop()
|
|
|
+ let option = '<option value="' + id + '">' + structure.name + '</option>';
|
|
|
+ federationSelect.append(option);
|
|
|
+ has_options = true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (has_options) {
|
|
|
+ federationSelect.prop("disabled", false);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- totalPages = Math.floor(totalStructures / itemsPerPage);
|
|
|
|
|
|
- // update the 'goto last page' link
|
|
|
- gotoLastPage.data("page", totalPages);
|
|
|
+ // #### Display results
|
|
|
+ const itemsPerPage = 10;
|
|
|
+ let currentPage = 1;
|
|
|
+
|
|
|
+ updateQuery();
|
|
|
|
|
|
// on page link clicked event
|
|
|
let pageLinkClicked = function(e) {
|
|
|
e.preventDefault();
|
|
|
let page = $(this).data('page');
|
|
|
- showPage(page);
|
|
|
+ if (page > 0) {
|
|
|
+ currentPage = page;
|
|
|
+ }
|
|
|
+ refresh();
|
|
|
$('body,html').animate({scrollTop: 0},500);
|
|
|
}
|
|
|
+ gotoFirstPage.on('click', pageLinkClicked);
|
|
|
+ gotoLastPage.on('click', pageLinkClicked);
|
|
|
|
|
|
- gotoFirstPage.on('click', pageLinkClicked)
|
|
|
- gotoLastPage.on('click', pageLinkClicked)
|
|
|
-
|
|
|
- function showPage(page) {
|
|
|
-
|
|
|
- let index = 0;
|
|
|
- $('.ot-structures-frame .structures-page .structure-card').remove();
|
|
|
+ let refresh = function() {
|
|
|
|
|
|
+ // Reinitialize current results
|
|
|
pleaseWaitSpan.show();
|
|
|
|
|
|
+ resultsPageDiv.find('.structure-card').remove();
|
|
|
+ paginationList.find('.goto-page-li').remove();
|
|
|
+ paginationList.hide();
|
|
|
+ if (clusters !== null) {
|
|
|
+ map.removeLayer(clusters);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ** Update results
|
|
|
+ let results = [];
|
|
|
structures.forEach(function(structure) {
|
|
|
+ if (matchCurrentQuery(structure)) {
|
|
|
+ results.push(structure)
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- if (((page - 1) * itemsPerPage) <= index && index < (page * itemsPerPage)) {
|
|
|
+ // ** Show the results for the current page
|
|
|
+ let index = 0;
|
|
|
|
|
|
+ results.forEach(function(structure) {
|
|
|
+ if (((currentPage - 1) * itemsPerPage) <= index && index < (currentPage * itemsPerPage)) {
|
|
|
let cardDiv = $(cardDivModel).clone();
|
|
|
|
|
|
cardDiv.data('id', structure.id);
|
|
|
|
|
|
let categoryTagModel = cardDiv.find('.structure-category-model').first();
|
|
|
- structure.categories.forEach(function(cat) {
|
|
|
+ structure.categories.forEach(function (cat) {
|
|
|
let tag = categoryTagModel.clone();
|
|
|
tag.text(structures_categories[cat]);
|
|
|
tag.removeClass('structure-category-model')
|
|
|
@@ -134,12 +245,27 @@ $(document).ready(function() {
|
|
|
index++;
|
|
|
});
|
|
|
|
|
|
- // Update the pagination
|
|
|
- $('.ot-structures-frame .pagination-list .goto-page-li').remove();
|
|
|
+ // ** Update results on map
|
|
|
+ clusters = L.markerClusterGroup();
|
|
|
+
|
|
|
+ results.forEach(function (item) {
|
|
|
+ if (item.longitude && item.latitude) {
|
|
|
+ let marker = L.marker([item.latitude, item.longitude]);
|
|
|
+ marker.bindPopup(`<b>${item.name}</b><br/>${item.postalCode} ${item.addressCity}<br/><a href="${item.website}" target="_blank">${item.website}</a>`);
|
|
|
+ clusters.addLayer(marker);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ map.addLayer(clusters);
|
|
|
+
|
|
|
+ // ** Update pagination
|
|
|
+ let resultsCount = results.length;
|
|
|
+ let pagesCount = Math.floor(resultsCount / itemsPerPage) + 1;
|
|
|
+ gotoLastPage.data("page", pagesCount);
|
|
|
+
|
|
|
let pageLiModel = paginationList.find('.goto-page-li-model').first();
|
|
|
|
|
|
- page_min = page > 5 ? page - 5 : 1;
|
|
|
- page_max = page < (totalPages - 5) ? page + 5 : totalPages;
|
|
|
+ let page_min = currentPage > 5 ? currentPage - 5 : 1;
|
|
|
+ let page_max = currentPage < (pagesCount - 5) ? currentPage + 5 : pagesCount;
|
|
|
|
|
|
for (let i = page_min; i <= page_max; i++) {
|
|
|
let pageLi = pageLiModel.clone();
|
|
|
@@ -150,7 +276,7 @@ $(document).ready(function() {
|
|
|
pageLink.attr("data-page", i);
|
|
|
pageLink.on('click', pageLinkClicked)
|
|
|
|
|
|
- if (i === page) {
|
|
|
+ if (i === currentPage) {
|
|
|
pageLi.addClass('current');
|
|
|
}
|
|
|
pageLi.removeClass('goto-page-li-model')
|
|
|
@@ -160,10 +286,59 @@ $(document).ready(function() {
|
|
|
|
|
|
pageLi.appendTo(paginationList);
|
|
|
}
|
|
|
+ paginationBar.show()
|
|
|
+ paginationList.show()
|
|
|
+
|
|
|
+ // Finalize
|
|
|
pleaseWaitSpan.hide();
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- showPage(1);
|
|
|
+ // #### Load structures data and refresh
|
|
|
+ var structures = [];
|
|
|
+ $.ajax({
|
|
|
+ type: 'GET',
|
|
|
+ url: apiGetUrl,
|
|
|
+ dataType: "json",
|
|
|
+ contentType: "application/json; charset=utf-8"
|
|
|
+ })
|
|
|
+ .done(function(res) {
|
|
|
+ structures = res["hydra:member"];
|
|
|
+ console.log(structures);
|
|
|
+ populateFederationsSelect();
|
|
|
+ refresh();
|
|
|
+ })
|
|
|
+ .fail(function() {
|
|
|
+ structureFrame.find(".error-message").show()
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ // #### Events
|
|
|
+
|
|
|
+ // Update query
|
|
|
+ form.on('submit', function(e) {
|
|
|
+ e.preventDefault();
|
|
|
+ updateQuery();
|
|
|
+ currentPage = 1;
|
|
|
+ refresh();
|
|
|
+ });
|
|
|
+
|
|
|
+ $('button[name="submit-search"]').on('click', function() {
|
|
|
+ form.submit();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Reset search fields
|
|
|
+ $('.reset-search').on('click', function (e) {
|
|
|
+ e.preventDefault();
|
|
|
+ let form = $(this).closest('form');
|
|
|
+ form.find('input').each(function () {
|
|
|
+ $(this).val('');
|
|
|
+ });
|
|
|
+ form.find('select').each(function () {
|
|
|
+ $(this).val('');
|
|
|
+ });
|
|
|
+ form.submit();
|
|
|
+ });
|
|
|
|
|
|
// Map goto commands
|
|
|
$('img[data-map-fit]').on('click', function (e) {
|
|
|
@@ -201,26 +376,84 @@ $(document).ready(function() {
|
|
|
})
|
|
|
|
|
|
// Form
|
|
|
- $('.search-submit').on('click', function() {
|
|
|
- $('#structure-search-form').submit();
|
|
|
+
|
|
|
+ // city search
|
|
|
+ let addressApiUrl = "https://api-adresse.data.gouv.fr/search/?type=municipality&autocomplete=1&limit=5&q=";
|
|
|
+
|
|
|
+ let resultDropdownDiv = form.find('.city-search-dropdown').first();
|
|
|
+ let input_name = resultDropdownDiv.siblings("input[name='search-city']").first();
|
|
|
+ let input_lat = resultDropdownDiv.siblings("input[name='lat']").first();
|
|
|
+ let input_long = resultDropdownDiv.siblings("input[name='long']").first();
|
|
|
+ let resultDiv = resultDropdownDiv.find('.city-search-results').first();
|
|
|
+ let loadingDiv = resultDropdownDiv.find('.city-search-loading').first();
|
|
|
+ let noResultDiv = resultDropdownDiv.find('.city-search-no-result').first();
|
|
|
+
|
|
|
+ $('body').click(function() {
|
|
|
+ resultDropdownDiv.hide();
|
|
|
+
|
|
|
+ // if no city was selected, clear the input
|
|
|
+ if (!input_lat.val()) {
|
|
|
+ input_name.val('');
|
|
|
+ resultDiv.empty();
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
- // Filters updated
|
|
|
- $('form select').on('change', function (e) {
|
|
|
- e.preventDefault();
|
|
|
- $(this).closest('form').submit();
|
|
|
+ input_name.click(function(e) {
|
|
|
+ if (resultDiv.children('.city-search-item').length > 0) {
|
|
|
+ resultDropdownDiv.show();
|
|
|
+ }
|
|
|
+ e.stopPropagation();
|
|
|
});
|
|
|
|
|
|
- // Reset search fields
|
|
|
- $('.reset-search').on('click', function (e) {
|
|
|
- e.preventDefault();
|
|
|
- let form = $(this).closest('form');
|
|
|
- form.find('input[type="text"]').each(function () {
|
|
|
- $(this).val('');
|
|
|
- });
|
|
|
- form.find('select').each(function () {
|
|
|
- $(this).val('');
|
|
|
+ $('.city-search-results').on('click', '.city-search-item', function (e) {
|
|
|
+ input_name.val($(this).text());
|
|
|
+ input_long.val($(this).data("x"));
|
|
|
+ input_lat.val($(this).data("y"));
|
|
|
+ });
|
|
|
+
|
|
|
+ $("input[name='search-city']").on('input', function(e) {
|
|
|
+ let query = $(this).val();
|
|
|
+ let url = addressApiUrl + encodeURI(query);
|
|
|
+
|
|
|
+ if (!query) {
|
|
|
+ resultDropdownDiv.hide();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ input_lat.val('');
|
|
|
+ input_long.val('');
|
|
|
+ loadingDiv.show();
|
|
|
+ resultDiv.empty();
|
|
|
+ resultDropdownDiv.show();
|
|
|
+ noResultDiv.hide();
|
|
|
+
|
|
|
+ $.ajax({
|
|
|
+ type: 'GET',
|
|
|
+ url: url,
|
|
|
+ dataType: "json",
|
|
|
+ contentType: "application/json; charset=utf-8"
|
|
|
+ })
|
|
|
+ .done(function(res) {
|
|
|
+ let features = res.features;
|
|
|
+ if (!features.length > 0) {
|
|
|
+ noResultDiv.show();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ features.forEach(function (f) {
|
|
|
+ let x = f.geometry.coordinates[0];
|
|
|
+ let y = f.geometry.coordinates[1];
|
|
|
+ let name = f.properties.name;
|
|
|
+ let postcode = f.properties.postcode;
|
|
|
+ let span = '<div class="city-search-item" data-x="' + x + '" data-y="' + y + '">' + name + ' (' + postcode + ')</div>';
|
|
|
+ resultDiv.append(span);
|
|
|
+ });
|
|
|
+ loadingDiv.hide();
|
|
|
+ })
|
|
|
+ .fail(function(e) {
|
|
|
+ let span = "<div>Une erreur s'est produite</div>";
|
|
|
+ console.error(e);
|
|
|
+ resultDiv.append(span);
|
|
|
+ loadingDiv.hide();
|
|
|
});
|
|
|
- form.submit();
|
|
|
});
|
|
|
});
|