From 65f471901bd04f38646031698665e6bccca4d22b Mon Sep 17 00:00:00 2001
From: "ext.sopra.ncastejon" <castejon.nicolas@gmail.com>
Date: Fri, 14 Feb 2020 14:02:42 +0100
Subject: [PATCH] Create a shared component for Download button.

---
 .../resource-queryable.component.html         |  17 +--
 .../resource-queryable.component.scss         |  42 ++----
 .../resource-queryable.component.ts           |  25 ++--
 .../dataset-downloads.component.html          |  12 +-
 .../dataset-downloads.component.spec.ts       |  25 ----
 .../dataset-downloads.component.ts            |   6 +-
 .../resource-custom-download.component.html   |  10 --
 .../resource-custom-download.component.scss   |   3 -
 .../resource-custom-download.component.ts     | 141 ------------------
 .../resource-download-item.component.html     |  46 +-----
 .../resource-download-item.component.scss     |  60 --------
 .../resource-download-item.component.ts       |  88 +++++++++--
 .../resource-static.component.html            |   8 -
 .../resource-static.component.scss            |   5 -
 .../resource-static.component.ts              |  33 ----
 src/app/dataset-detail/components/index.ts    |   6 +-
 .../download-button.component.html            |  25 ++++
 .../download-button.component.scss            |  59 ++++++++
 .../download-button.component.ts              |  77 ++++++++++
 src/app/shared/components/index.ts            |   4 +-
 20 files changed, 293 insertions(+), 399 deletions(-)
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.spec.ts
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.html
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.scss
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.ts
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.html
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.scss
 delete mode 100644 src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.ts
 create mode 100644 src/app/shared/components/download-button/download-button.component.html
 create mode 100644 src/app/shared/components/download-button/download-button.component.scss
 create mode 100644 src/app/shared/components/download-button/download-button.component.ts

diff --git a/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.html b/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.html
index 2b9b422f..8173abc8 100644
--- a/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.html
+++ b/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.html
@@ -206,23 +206,12 @@
                clip-rule="evenodd" />
            </svg>
          </button>
-         <div class="column resource-download-icon is-narrow-tablet is-5-mobile" (click)="saveFile(queryableUrl)"
-           [attr.data-tooltip]="downloadMessage">
-
-           <a>
-             <svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" fill="none" viewBox="0 0 22 21">
-               <path fill="#4668AB"
-                 d="M21.323 13.961h-.338c-.339 0-.677.339-.677.677v4.57H1.692v-4.57a.667.667 0 0 0-.677-.677H.677c-.339 0-.677.339-.677.677V20.223c0 .423.338.677.677.677h20.646c.338 0 .677-.339.677-.677v-5.585a.667.667 0 0 0-.677-.677z" />
-               <path fill="#4668AB"
-                 d="M10.323 15.315c.085.085.254.17.339.17.084.084.254.084.338.084.085 0 .254 0 .339-.084.17 0 .254-.085.338-.17L17.6 9.223c.17-.254.17-.592 0-.761l-.423-.424c-.169-.253-.592-.253-.761 0l-4.57 4.654V.846C11.847.338 11.509 0 11 0c-.507 0-.846.338-.846.846v11.846L5.585 8.038c-.17-.253-.508-.253-.762 0l-.423.424c-.169.253-.169.592 0 .761l5.923 6.092z" />
-             </svg>
-           </a>
+         <div class="resource-download-icon">
+           <app-download-button [url]="queryableUrl" [fileName]="getFileName()">
+           </app-download-button>
          </div>
 
-
        </div>
-
-
      </div>
 
    </ng-container>
diff --git a/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.scss b/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.scss
index 8bbedd26..c26e04a0 100644
--- a/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.scss
+++ b/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.scss
@@ -50,9 +50,9 @@
     border: 1px solid $grey-super-light-color;
     transition: all 0.5s ease;
     word-break: break-all;
-    // flex-grow: 1;
     flex-shrink: 1;
     padding: 0.7rem 0.5rem;
+    min-height: 2.5rem;
 
     // ng-deep needed because of the innerHtml breaks the styling
     /* stylelint-disable-next-line */
@@ -77,9 +77,18 @@
   .button {
     font-size: 0.875rem;
     margin-left: 0.5rem;
-    height: 2.5rem;
-    width: 2.5rem;
     position: relative;
+    margin-right: 0.5rem;
+    height: 3.5rem;
+    flex-grow: 1;
+    margin-top: 0.5rem;
+
+    @media screen and (min-width: $tablet) {
+      height: 2.5rem;
+      width: 2.5rem;
+      margin-top: 0;
+      flex-grow: 0;
+    }
 
     svg {
       position: absolute;
@@ -150,26 +159,6 @@
   word-break: break-all;
 }
 
-.resource-download-icon {
-  padding: 0.5rem 0.4rem;
-  border: 1px solid $grey-super-light-color;
-  border-radius: 4px;
-
-  a {
-    display: flex;
-    justify-content: center;
-  }
-
-  svg {
-    height: 1.5rem;
-    fill: $link-color;
-  }
-
-  &:hover a {
-    opacity: 0.7;
-  }
-}
-
 .create-request {
   cursor: pointer;
   display: flex;
@@ -218,11 +207,12 @@
     .tooltip {
       margin-left: 0;
       margin-top: 0.5rem;
-      width: 2.5rem;
-      height: 2.5rem;
+      height: 3.5rem;
+      flex-grow: 1;
 
       @media screen and (min-width: $tablet) {
-        margin-left: 0.5rem;
+        width: 2.5rem;
+        height: 2.5rem;
         margin-top: 0;
       }
     }
diff --git a/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.ts b/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.ts
index 883f6a66..1bf86c31 100644
--- a/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.ts
+++ b/src/app/dataset-detail/components/dataset-api/resources-queryable/resource-queryable/resource-queryable.component.ts
@@ -1,8 +1,7 @@
 import { Component, Input, OnInit } from '@angular/core';
 import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
-import { saveAs } from 'file-saver';
 import { communeInsee } from '../../../../../../assets/resources/commune-insee';
-import { geosource, notificationMessages } from '../../../../../../i18n/traductions';
+import { geosource } from '../../../../../../i18n/traductions';
 import { NotificationService } from '../../../../../core/services';
 import { linkFormats, Metadata } from '../../../../../shared/models';
 import { Format, Projection, Resource } from '../../../../models';
@@ -451,16 +450,20 @@ export class ResourceQueryableComponent implements OnInit {
     return array;
   }
 
-  saveFile(url: string) {
-    saveAs(url, `${this.resource.metadataLink.name}.${this.selectedFormat.fileExtension}`);
-    this._notificationService.notify(
-      {
-        type: 'success',
-        message: notificationMessages.general.startDownload,
-      },
-    );
+  getFileName() {
+    return `${this.resource.metadataLink.name}.${this.selectedFormat.fileExtension}`;
   }
 
+  // saveFile(url: string) {
+  //   saveAs(url, `${this.resource.metadataLink.name}.${this.selectedFormat.fileExtension}`);
+  //   this._notificationService.notify(
+  //     {
+  //       type: 'success',
+  //       message: notificationMessages.general.startDownload,
+  //     },
+  //   );
+  // }
+
   get totalData() {
     return this._datasetDetailService.dataset.totalData;
   }
@@ -488,11 +491,13 @@ export class ResourceQueryableComponent implements OnInit {
       this.messageClipboardRoot = geosource.mapMessages.copied;
       setTimeout(() => {
         this.messageClipboardRoot = geosource.mapMessages.share;
+      // tslint:disable-next-line: align
       }, 2000);
     } else {
       this.messageClipboard = geosource.mapMessages.copied;
       setTimeout(() => {
         this.messageClipboard = geosource.mapMessages.share;
+      // tslint:disable-next-line: align
       }, 2000);
     }
 
diff --git a/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.html b/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.html
index 98333e5f..010e5e95 100644
--- a/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.html
+++ b/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.html
@@ -8,8 +8,10 @@
             <span>{{downloadable.key}}</span>
           </div>
           <div class="resource-downloadable-files" *ngFor="let resource of downloadable.value">
-            <app-resource-custom-download [resource]="resource">
-            </app-resource-custom-download>
+         <ng-container *ngFor="let format of resource.formats; let i=index">
+           <app-resource-download-item [format]="format" [resource]="resource" [isQueryable]="true">
+           </app-resource-download-item>
+         </ng-container>
           </div>
         </div>
       </div>
@@ -19,9 +21,9 @@
           <div class="resource-description">
             <span>{{static.key}}</span>
           </div>
-          <div class="resource-static-files" *ngFor="let resource of static.value">
-            <app-resource-static [link]="resource">
-            </app-resource-static>
+          <div class="resource-static-files" *ngFor="let metadataLink of static.value">
+               <app-resource-download-item [link]="metadataLink" [isQueryable]="false">
+               </app-resource-download-item>
           </div>
         </div>
       </div>
diff --git a/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.spec.ts b/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.spec.ts
deleted file mode 100644
index aae8691b..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { DatasetDownloadsComponent } from './dataset-downloads.component';
-
-describe('DatasetDownloadsComponent', () => {
-  let component: DatasetDownloadsComponent;
-  let fixture: ComponentFixture<DatasetDownloadsComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ DatasetDownloadsComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(DatasetDownloadsComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.ts b/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.ts
index 875ea3a8..9bc4fc7c 100644
--- a/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.ts
+++ b/src/app/dataset-detail/components/dataset-downloads/dataset-downloads.component.ts
@@ -5,7 +5,7 @@ import { environment } from '../../../../environments/environment';
 import { notificationMessages } from '../../../../i18n/traductions';
 import { Notification } from '../../../core/models';
 import { NotificationService } from '../../../core/services';
-import { IMetadataLink, Metadata } from '../../../shared/models';
+import { IMetadataLink, linkFormats, Metadata } from '../../../shared/models';
 import { UserService } from '../../../user/services';
 import { Resource } from '../../models';
 import { DatasetDetailService, ResourcesService } from '../../services';
@@ -17,6 +17,8 @@ import { DatasetDetailService, ResourcesService } from '../../services';
 })
 export class DatasetDownloadsComponent implements OnInit {
 
+  linkFormats = linkFormats;
+
   metadata: Metadata;
   sub: Subscription;
   downloadableResources: Resource[];
@@ -99,7 +101,7 @@ export class DatasetDownloadsComponent implements OnInit {
               if (link.service === resource.type && resourceCopy.isQueryable) {
 
                 // We exclude Shapefile if from WFS. The Shapefile from WS has priority.
-                if (resourceCopy.type === 'WFS') {
+                if (resourceCopy.type === this.linkFormats.wfs) {
                   resourceCopy.formats = resourceCopy.formats.filter(f => f.name !== 'ShapeFile');
                 }
 
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.html b/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.html
deleted file mode 100644
index 96578bcd..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="resource">
-
-  <ng-container *ngFor="let format of resource.formats; let i=index">
-    <app-resource-download-item [formatName]="format.name" [formatExtension]="format.fileExtension"
-      [resourceName]="resource.metadataLink.name" [isQueryable]="true" (saveEvent)="saveFile(format, i)"
-      (abortEvent)="abortDownload(i)"
-      [whichStepLoading]="whichStepLoading[i]">
-    </app-resource-download-item>
-  </ng-container>
-</div>
\ No newline at end of file
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.scss b/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.scss
deleted file mode 100644
index e4ab1d66..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.resource {
-  background-color: white;
-}
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.ts b/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.ts
deleted file mode 100644
index 8dc0e1fc..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/resource-custom-download/resource-custom-download.component.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import { Component, HostListener, Input, OnDestroy, OnInit } from '@angular/core';
-import { NotificationService } from '../../../../core/services';
-import { linkFormats } from '../../../../shared/models';
-import { Format, Resource } from '../../../models';
-
-@Component({
-  selector: 'app-resource-custom-download',
-  templateUrl: './resource-custom-download.component.html',
-  styleUrls: ['./resource-custom-download.component.scss'],
-})
-export class ResourceCustomDownloadableComponent implements OnInit, OnDestroy {
-  linkFormats = linkFormats;
-
-  @Input() resource: Resource;
-
-  // Event fired when one tab is closed or refreshed. We abort the on-going request
-  @HostListener('window:beforeunload')
-  abortCurrentRequest() {
-    this.controller.abort();
-  }
-
-  // Variable to manage the icon display for each resource. Possible values:
-  // - 'loading': display the spinner
-  // - 'cancel': display the cross to abort the on-going request
-  // -  null: display the download icon
-  whichStepLoading: string[];
-  signal: AbortSignal;
-  controller: AbortController;
-  timeOut: number;
-
-  labelLayer = {
-    WFS: 'typename',
-    WMS: 'layers',
-    WCS: 'identifiers',
-  };
-
-  labelFormat = {
-    WFS: 'outputFormat',
-    WMS: 'FORMAT',
-    WCS: 'FORMAT',
-  };
-
-  constructor(
-    private _notificationService: NotificationService) { }
-
-  ngOnInit() {
-    this.whichStepLoading = Array(this.resource.formats.length);
-    // Used to abort the request if needed
-    this.controller = new AbortController();
-    this.signal = this.controller.signal;
-  }
-
-  ngOnDestroy() {
-    this.controller.abort();
-  }
-
-  saveFile(format: Format, index: number) {
-    this.whichStepLoading[index] = 'loading';
-
-    fetch(this.getQueryableUrl(format), { signal: this.signal }).then((response) => {
-      // To allow the download, get the repsons as a blob
-      return response.blob();
-    })
-      .then((blob) => {
-        // Create a temporary link and click on it to launch the blob download
-        const url = window.URL.createObjectURL(blob);
-        const a = document.createElement('a');
-        a.href = url;
-        a.rel = 'noopener';
-        a.download = `${this.resource.metadataLink.name}.${format.fileExtension}`;
-        document.body.appendChild(a); // append the element to the dom -> otherwise it will not work in firefox
-        a.click();
-        a.remove();
-        this.whichStepLoading[index] = null;
-        clearTimeout(this.timeOut); // If the download is under 2000ms, we clear the ongoing timeout
-      })
-      .catch((err) => {
-        console.log('The fetch could not succeeded', err.message);
-      });
-  }
-
-  abortDownload(index: number) {
-    this.controller.abort();
-    this.whichStepLoading[index] = null;
-
-    // Create a new for a new DOM Request
-    this.controller = new AbortController();
-    this.signal = this.controller.signal;
-
-  }
-
-  getQueryableUrl(format: Format) {
-    let queryableUrl = this.resource.metadataLink.url;
-    let layer = '';
-    let outputFormat = '';
-    if (this.resource.isStandard) {
-      layer = `&${this.labelLayer[this.resource.type]}=${this.resource.metadataLink.name}`;
-      outputFormat = `&${this.labelFormat[this.resource.type]}=` +
-        `${format.mapServerType}`;
-      const projectionAndBbox = this.getProjectionAndBbox(format);
-
-      const baseParameters = this.resource.parametersUrl ? `?${this.resource.parametersUrl}` : '';
-      queryableUrl += baseParameters +
-        layer +
-        outputFormat +
-        projectionAndBbox.projection +
-        projectionAndBbox.bboxRequest;
-    } else if (this.resource.type === linkFormats.ws) {
-      if (format.name === 'JSON') {
-        queryableUrl += `/${this.resource.metadataLink.name}/all.json`;
-      } else {
-        queryableUrl += `/${this.resource.metadataLink.name}.shp?srsname=EPSG:3946`;
-      }
-    }
-    return queryableUrl;
-  }
-
-  getProjectionAndBbox(format: Format) {
-    let projection = '';
-    let bboxRequest = '';
-    if (this.resource.type === linkFormats.wfs) {
-      projection = '&SRSNAME=EPSG:3946';
-    } else if (this.resource.type === linkFormats.wms || this.resource.type === linkFormats.wcs) {
-      projection = '&CRS=EPSG:3946';
-      const bbox = this.resource.metadataLink.bbox_by_projection['EPSG:3946'];
-      bboxRequest = `&BBOX=${bbox.minx},` +
-        `${bbox.miny},` +
-        `${bbox.maxx},` +
-        `${bbox.maxy}`;
-    } else if (this.resource.type === linkFormats.ws) {
-      if (format.name !== 'JSON') {
-        projection = 'srsname=EPSG:3946';
-      }
-    }
-
-    return {
-      projection,
-      bboxRequest,
-    };
-  }
-}
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.html b/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.html
index f47193a1..9a23a375 100644
--- a/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.html
+++ b/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.html
@@ -1,54 +1,16 @@
  <div class="resource-download">
    <div class="resource-left">
-     <app-format-icon [formatName]="formatName">
+     <app-format-icon [formatName]="getFormatName()">
      </app-format-icon>
      <div class="resource-main">
        <div class="resource-main-name">
-         <span>{{isQueryable ? resourceName + '.' + formatExtension : resourceName }}
+         <span>{{isQueryable ? resource.metadataLink.name + '.' + format.fileExtension : link.name }}
          </span>
        </div>
      </div>
    </div>
 
-   <ng-container *ngIf="isQueryable; else other">
-
-   <div class="resource-icon-download" (click)="saveResource()" *ngIf="!whichStepLoading">
-     <a [attr.data-tooltip]="downloadMessage">
-       <svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" fill="none" viewBox="0 0 22 21">
-         <path fill="#4668AB"
-           d="M21.323 13.961h-.338c-.339 0-.677.339-.677.677v4.57H1.692v-4.57a.667.667 0 0 0-.677-.677H.677c-.339 0-.677.339-.677.677V20.223c0 .423.338.677.677.677h20.646c.338 0 .677-.339.677-.677v-5.585a.667.667 0 0 0-.677-.677z" />
-         <path fill="#4668AB"
-           d="M10.323 15.315c.085.085.254.17.339.17.084.084.254.084.338.084.085 0 .254 0 .339-.084.17 0 .254-.085.338-.17L17.6 9.223c.17-.254.17-.592 0-.761l-.423-.424c-.169-.253-.592-.253-.761 0l-4.57 4.654V.846C11.847.338 11.509 0 11 0c-.507 0-.846.338-.846.846v11.846L5.585 8.038c-.17-.253-.508-.253-.762 0l-.423.424c-.169.253-.169.592 0 .761l5.923 6.092z" />
-       </svg>
-     </a>
-   </div>
-   <div class="resource-icon-cancel" *ngIf="whichStepLoading"
-     [attr.data-tooltip]="whichStepLoading ? abortMessage : null">
-
-     <svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
-       <path fill-rule="evenodd" clip-rule="evenodd"
-         d="M17 0C18.1009 0 19.0325 0.929231 19.0325 2.02741C19.0325 3.16783 18.1009 4.05483 17 4.05483C15.8567 4.05483 14.9675 3.16783 14.9675 2.02741C14.9675 0.929231 15.8567 0 17 0ZM6.37132 4.36171C7.47224 4.36171 8.40378 5.29094 8.40378 6.38912C8.40378 7.52954 7.47224 8.41653 6.37132 8.41653C5.22807 8.41653 4.33887 7.52954 4.33887 6.38912C4.33887 5.29094 5.22807 4.36171 6.37132 4.36171ZM4.06492 17.0002C4.06492 15.902 3.13337 14.9728 2.03246 14.9728C0.8892 14.9728 0 15.902 0 17.0002C0 18.1406 0.8892 19.0276 2.03246 19.0276C3.13337 19.0276 4.06492 18.1406 4.06492 17.0002ZM6.37132 25.5838C7.47224 25.5838 8.40378 26.513 8.40378 27.6112C8.40378 28.7516 7.47224 29.6386 6.37132 29.6386C5.22807 29.6386 4.33887 28.7516 4.33887 27.6112C4.33887 26.513 5.22807 25.5838 6.37132 25.5838ZM19.0327 31.9727C19.0327 30.8745 18.1011 29.9453 17.0002 29.9453C15.857 29.9453 14.9678 30.8745 14.9678 31.9727C14.9678 33.1131 15.857 34.0001 17.0002 34.0001C18.1011 34.0001 19.0327 33.1131 19.0327 31.9727ZM27.6301 25.5838C28.731 25.5838 29.6626 26.513 29.6626 27.6112C29.6626 28.7516 28.731 29.6386 27.6301 29.6386C26.4869 29.6386 25.5977 28.7516 25.5977 27.6112C25.5977 26.513 26.4869 25.5838 27.6301 25.5838ZM34.0005 17.0002C34.0005 15.902 33.0689 14.9728 31.968 14.9728C30.8247 14.9728 29.9355 15.902 29.9355 17.0002C29.9355 18.1406 30.8247 19.0276 31.968 19.0276C33.0689 19.0276 34.0005 18.1406 34.0005 17.0002Z"
-         fill="#242B3F" />
-       <animateTransform attributeName="transform" type="rotate" from="0 0 0" to="360 0 0" dur="1.2s"
-         repeatCount="indefinite" />
-     </svg>
-
-     <img src="../../../../../../assets/img/close.svg" [alt]="abortMessage" (click)=abortDownload()>
-   </div>
-   </ng-container>
-
-   <ng-template #other>
-     <div class="resource-icon-download">
-       <a [href]="resourceUrl" target="_blank">
-         <svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" fill="none" viewBox="0 0 22 21">
-           <path fill="#4668AB"
-             d="M21.323 13.961h-.338c-.339 0-.677.339-.677.677v4.57H1.692v-4.57a.667.667 0 0 0-.677-.677H.677c-.339 0-.677.339-.677.677V20.223c0 .423.338.677.677.677h20.646c.338 0 .677-.339.677-.677v-5.585a.667.667 0 0 0-.677-.677z" />
-           <path fill="#4668AB"
-             d="M10.323 15.315c.085.085.254.17.339.17.084.084.254.084.338.084.085 0 .254 0 .339-.084.17 0 .254-.085.338-.17L17.6 9.223c.17-.254.17-.592 0-.761l-.423-.424c-.169-.253-.592-.253-.761 0l-4.57 4.654V.846C11.847.338 11.509 0 11 0c-.507 0-.846.338-.846.846v11.846L5.585 8.038c-.17-.253-.508-.253-.762 0l-.423.424c-.169.253-.169.592 0 .761l5.923 6.092z" />
-         </svg>
-       </a>
-     </div>
-   </ng-template>
-
+   <app-download-button [url]="getQueryableUrl()" [fileName]="getFileName()">
+   </app-download-button>
 
  </div>
\ No newline at end of file
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.scss b/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.scss
index 485b2645..6fcfcccf 100644
--- a/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.scss
+++ b/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.scss
@@ -29,63 +29,3 @@
     }
   }
 }
-
-.resource-icon-download {
-  padding: 0.4rem 0.4rem;
-  border: 1px solid $grey-super-light-color;
-  border-radius: 4px;
-  margin-right: 0.75rem;
-  width: 2.5rem;
-  height: 2.5rem;
-
-  a {
-    display: flex;
-  }
-
-  svg {
-    height: 1.5rem;
-    width: 1.5rem;
-    fill: $link-color;
-  }
-
-  &:hover svg {
-    opacity: 0.7;
-  }
-}
-
-
-.resource-icon-cancel {
-  padding: 0.4rem 0.4rem;
-  border: 1px solid $grey-super-light-color;
-  border-radius: 4px;
-  margin-right: 0.75rem;
-  position: relative;
-  width: 2.5rem;
-  height: 2.5rem;
-
-  a {
-    display: flex;
-  }
-
-  svg {
-    position: absolute;
-    height: 2rem;
-    width: 2rem;
-    left: calc(50% - 1rem);
-    top: calc(50% - 1rem);
-    fill: $link-color;
-    opacity: 0.6;
-  }
-
-  img {
-    position: absolute;
-    height: 0.875rem;
-    width: 0.875rem;
-    left: calc(50% - 0.4375rem);
-    top: calc(50% - 0.4375rem);
-  }
-
-  &:hover img {
-    opacity: 0.6;
-  }
-}
\ No newline at end of file
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.ts b/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.ts
index 57caccc9..2887c835 100644
--- a/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.ts
+++ b/src/app/dataset-detail/components/dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component.ts
@@ -1,5 +1,7 @@
 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 import { geosource } from '../../../../../../i18n/traductions';
+import { IMetadataLink, linkFormats } from '../../../../../shared/models';
+import { Format, Resource } from '../../../../models';
 
 @Component({
   selector: 'app-resource-download-item',
@@ -8,29 +10,97 @@ import { geosource } from '../../../../../../i18n/traductions';
 })
 export class ResourceDownloadItemComponent implements OnInit {
 
-  @Input() formatName: string;
-  @Input() formatExtension?: string;
-  @Input() resourceName: string;
-  @Input() resourceUrl ?: string;
+  linkFormats = linkFormats;
+
+  @Input() format?: Format;
+  @Input() resource: Resource;
+  @Input() link: IMetadataLink;
   @Input() isQueryable: boolean;
-  @Input() whichStepLoading ?: string;
   @Output() saveEvent = new EventEmitter();
   @Output() abortEvent = new EventEmitter();
 
   abortMessage: string = geosource.downloads.abort;
   downloadMessage: string = geosource.downloads.download;
 
+  labelLayer = {
+    WFS: 'typename',
+    WMS: 'layers',
+    WCS: 'identifiers',
+  };
+
+  labelFormat = {
+    WFS: 'outputFormat',
+    WMS: 'FORMAT',
+    WCS: 'FORMAT',
+  };
+
   constructor() { }
 
   ngOnInit() {
   }
 
-  saveResource() {
-    this.saveEvent.emit(null);
+  getFormatName() {
+    return this.format ? this.format.name : this.link.formats[0];
+  }
+
+  getQueryableUrl() {
+    let queryableUrl = '';
+    if (this.isQueryable) {
+
+      queryableUrl = this.resource.metadataLink.url;
+      let layer = '';
+      let outputFormat = '';
+      if (this.resource.isStandard) {
+        layer = `&${this.labelLayer[this.resource.type]}=${this.resource.metadataLink.name}`;
+        outputFormat = `&${this.labelFormat[this.resource.type]}=` +
+          `${this.format.mapServerType}`;
+        const projectionAndBbox = this.getProjectionAndBbox(this.format);
+
+        const baseParameters = this.resource.parametersUrl ? `?${this.resource.parametersUrl}` : '';
+        queryableUrl += baseParameters +
+          layer +
+          outputFormat +
+          projectionAndBbox.projection +
+          projectionAndBbox.bboxRequest;
+      } else if (this.resource.type === linkFormats.ws) {
+        if (this.format.name === 'JSON') {
+          queryableUrl += `/${this.resource.metadataLink.name}/all.json`;
+        } else {
+          queryableUrl += `/${this.resource.metadataLink.name}.shp?srsname=EPSG:3946`;
+        }
+      }
+    } else {
+      queryableUrl = this.link.url;
+    }
+    return queryableUrl;
   }
 
-  abortDownload() {
-    this.abortEvent.emit(null);
+  getFileName() {
+    return this.resource ? `${this.resource.metadataLink.name}.${this.format.fileExtension}` : this.link.name;
+  }
+
+  getProjectionAndBbox(format: Format) {
+    let projection = '';
+    let bboxRequest = '';
+    if (this.resource.type === linkFormats.wfs) {
+      projection = '&SRSNAME=EPSG:3946';
+    } else if (this.resource.type === linkFormats.wms || this.resource.type === linkFormats.wcs) {
+      projection = '&CRS=EPSG:3946';
+      const bbox = this.resource.metadataLink.bbox_by_projection['EPSG:3946'];
+      bboxRequest = `&BBOX=${bbox.minx},` +
+        `${bbox.miny},` +
+        `${bbox.maxx},` +
+        `${bbox.maxy}`;
+    } else if (this.resource.type === linkFormats.ws) {
+      if (format.name !== 'JSON') {
+        projection = 'srsname=EPSG:3946';
+      }
+    }
+
+    return {
+      projection,
+      bboxRequest,
+    };
   }
 
 }
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.html b/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.html
deleted file mode 100644
index ff97f01e..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="resource">
-
-  <app-resource-download-item [formatName]="link.formats[0]"
-    [resourceName]="link.name" [resourceUrl]="link.url" [isQueryable]="false">
-  </app-resource-download-item>
-
-
-</div>
\ No newline at end of file
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.scss b/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.scss
deleted file mode 100644
index 934aa980..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-@import '../../../../../scss/variables.scss';
-
-.resource {
-  background-color: white;
-}
diff --git a/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.ts b/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.ts
deleted file mode 100644
index c308d7de..00000000
--- a/src/app/dataset-detail/components/dataset-downloads/resource-static/resource-static.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { IMetadataLink } from '../../../../shared/models';
-
-@Component({
-  selector: 'app-resource-static',
-  templateUrl: './resource-static.component.html',
-  styleUrls: ['./resource-static.component.scss'],
-})
-export class ResourceStaticComponent implements OnInit {
-  @Input() link: IMetadataLink;
-
-  constructor() { }
-
-  ngOnInit() {
-  }
-
-  humanFileSize(bytes: string, si: boolean) {
-    let sizeFilebytes = Number(bytes);
-    const thresh = si ? 1000 : 1024;
-    if (Math.abs(Number((bytes))) < thresh) {
-      return `${bytes} B`;
-    }
-    const units = si
-      ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
-      : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
-    let u = -1;
-    do {
-      sizeFilebytes /= thresh;
-      u += 1;
-    } while (Math.abs(sizeFilebytes) >= thresh && u < units.length - 1);
-    return `${sizeFilebytes.toFixed(1)} ${units[u]}`;
-  }
-}
diff --git a/src/app/dataset-detail/components/index.ts b/src/app/dataset-detail/components/index.ts
index 8c51fbcd..3151d6a3 100644
--- a/src/app/dataset-detail/components/index.ts
+++ b/src/app/dataset-detail/components/index.ts
@@ -10,26 +10,22 @@ import { DatasetDataDetailsComponent } from './dataset-data-details/dataset-data
 import { DatasetDetailComponent } from './dataset-detail/dataset-detail.component';
 import { DatasetDownloadsComponent } from './dataset-downloads/dataset-downloads.component';
 // tslint:disable-next-line: max-line-length
-import { ResourceCustomDownloadableComponent } from './dataset-downloads/resource-custom-download/resource-custom-download.component';
 // tslint:disable-next-line: max-line-length
 import { ResourceDownloadItemComponent } from './dataset-downloads/resource-download-item/resource-download-item/resource-download-item.component';
-import { ResourceStaticComponent } from './dataset-downloads/resource-static/resource-static.component';
 import { DatasetInfoComponent } from './dataset-info/dataset-info.component';
 import { DatasetMapComponent } from './dataset-map/dataset-map.component';
 import { DatasetTableMapComponent } from './dataset-table-map/dataset-table-map.component';
 import { DatasetTableComponent } from './dataset-table/dataset-table.component';
 // tslint:disable-next-line: max-line-length
-export { DatasetDetailComponent, ResourceDownloadItemComponent, DatasetAPIComponent, ResourceCustomDownloadableComponent, ResourceStaticComponent, DatasetDownloadsComponent, ResourceQueryableComponent, DatasetMapComponent, DatasetInfoComponent, DatasetTableMapComponent, DatasetTableComponent, ResourcesQueryableComponent, IconFormatComponent, DatasetDataDetailsComponent, DatasetDataDetailPropertiesComponent };
+export { DatasetDetailComponent, ResourceDownloadItemComponent, DatasetAPIComponent, DatasetDownloadsComponent, ResourceQueryableComponent, DatasetMapComponent, DatasetInfoComponent, DatasetTableMapComponent, DatasetTableComponent, ResourcesQueryableComponent, IconFormatComponent, DatasetDataDetailsComponent, DatasetDataDetailPropertiesComponent };
 // tslint:disable-next-line:variable-name
 export const DatasetDetailComponents = [
   DatasetDetailComponent,
   ResourceQueryableComponent,
   DatasetAPIComponent,
-  ResourceCustomDownloadableComponent,
   DatasetMapComponent,
   DatasetInfoComponent,
   DatasetDownloadsComponent,
-  ResourceStaticComponent,
   DatasetTableMapComponent,
   DatasetTableComponent,
   ResourcesQueryableComponent,
diff --git a/src/app/shared/components/download-button/download-button.component.html b/src/app/shared/components/download-button/download-button.component.html
new file mode 100644
index 00000000..9d03438d
--- /dev/null
+++ b/src/app/shared/components/download-button/download-button.component.html
@@ -0,0 +1,25 @@
+<div class="download-button" [attr.data-tooltip]="loading ? abortMessage : null">
+
+  <div class="resource-icon-download" (click)="download()" *ngIf="!loading">
+    <a [attr.data-tooltip]="downloadMessage">
+      <svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" fill="none" viewBox="0 0 22 21">
+        <path fill="#4668AB"
+          d="M21.323 13.961h-.338c-.339 0-.677.339-.677.677v4.57H1.692v-4.57a.667.667 0 0 0-.677-.677H.677c-.339 0-.677.339-.677.677V20.223c0 .423.338.677.677.677h20.646c.338 0 .677-.339.677-.677v-5.585a.667.667 0 0 0-.677-.677z" />
+        <path fill="#4668AB"
+          d="M10.323 15.315c.085.085.254.17.339.17.084.084.254.084.338.084.085 0 .254 0 .339-.084.17 0 .254-.085.338-.17L17.6 9.223c.17-.254.17-.592 0-.761l-.423-.424c-.169-.253-.592-.253-.761 0l-4.57 4.654V.846C11.847.338 11.509 0 11 0c-.507 0-.846.338-.846.846v11.846L5.585 8.038c-.17-.253-.508-.253-.762 0l-.423.424c-.169.253-.169.592 0 .761l5.923 6.092z" />
+      </svg>
+    </a>
+  </div>
+  <div class="resource-icon-cancel" *ngIf="loading">
+
+    <svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
+      <path fill-rule="evenodd" clip-rule="evenodd"
+        d="M17 0C18.1009 0 19.0325 0.929231 19.0325 2.02741C19.0325 3.16783 18.1009 4.05483 17 4.05483C15.8567 4.05483 14.9675 3.16783 14.9675 2.02741C14.9675 0.929231 15.8567 0 17 0ZM6.37132 4.36171C7.47224 4.36171 8.40378 5.29094 8.40378 6.38912C8.40378 7.52954 7.47224 8.41653 6.37132 8.41653C5.22807 8.41653 4.33887 7.52954 4.33887 6.38912C4.33887 5.29094 5.22807 4.36171 6.37132 4.36171ZM4.06492 17.0002C4.06492 15.902 3.13337 14.9728 2.03246 14.9728C0.8892 14.9728 0 15.902 0 17.0002C0 18.1406 0.8892 19.0276 2.03246 19.0276C3.13337 19.0276 4.06492 18.1406 4.06492 17.0002ZM6.37132 25.5838C7.47224 25.5838 8.40378 26.513 8.40378 27.6112C8.40378 28.7516 7.47224 29.6386 6.37132 29.6386C5.22807 29.6386 4.33887 28.7516 4.33887 27.6112C4.33887 26.513 5.22807 25.5838 6.37132 25.5838ZM19.0327 31.9727C19.0327 30.8745 18.1011 29.9453 17.0002 29.9453C15.857 29.9453 14.9678 30.8745 14.9678 31.9727C14.9678 33.1131 15.857 34.0001 17.0002 34.0001C18.1011 34.0001 19.0327 33.1131 19.0327 31.9727ZM27.6301 25.5838C28.731 25.5838 29.6626 26.513 29.6626 27.6112C29.6626 28.7516 28.731 29.6386 27.6301 29.6386C26.4869 29.6386 25.5977 28.7516 25.5977 27.6112C25.5977 26.513 26.4869 25.5838 27.6301 25.5838ZM34.0005 17.0002C34.0005 15.902 33.0689 14.9728 31.968 14.9728C30.8247 14.9728 29.9355 15.902 29.9355 17.0002C29.9355 18.1406 30.8247 19.0276 31.968 19.0276C33.0689 19.0276 34.0005 18.1406 34.0005 17.0002Z"
+        fill="#242B3F" />
+      <animateTransform attributeName="transform" type="rotate" from="0 0 0" to="360 0 0" dur="1.2s"
+        repeatCount="indefinite" />
+    </svg>
+
+    <img src="../../../../../../assets/img/close.svg" [alt]="abortMessage" (click)=abortDownload()>
+  </div>
+</div>
\ No newline at end of file
diff --git a/src/app/shared/components/download-button/download-button.component.scss b/src/app/shared/components/download-button/download-button.component.scss
new file mode 100644
index 00000000..aff96884
--- /dev/null
+++ b/src/app/shared/components/download-button/download-button.component.scss
@@ -0,0 +1,59 @@
+@import './../../../../scss/variables.scss';
+
+.resource-icon-download {
+  padding: 0.4rem 0.4rem;
+  border: 1px solid $grey-super-light-color;
+  border-radius: 4px;
+  width: 2.5rem;
+  height: 2.5rem;
+
+  a {
+    display: flex;
+  }
+
+  svg {
+    height: 1.5rem;
+    width: 1.5rem;
+    fill: $link-color;
+  }
+
+  &:hover svg {
+    opacity: 0.7;
+  }
+}
+
+
+.resource-icon-cancel {
+  padding: 0.4rem 0.4rem;
+  border: 1px solid $grey-super-light-color;
+  border-radius: 4px;
+  position: relative;
+  width: 2.5rem;
+  height: 2.5rem;
+
+  a {
+    display: flex;
+  }
+
+  svg {
+    position: absolute;
+    height: 2rem;
+    width: 2rem;
+    left: calc(50% - 1rem);
+    top: calc(50% - 1rem);
+    fill: $link-color;
+    opacity: 0.6;
+  }
+
+  img {
+    position: absolute;
+    height: 0.875rem;
+    width: 0.875rem;
+    left: calc(50% - 0.4375rem);
+    top: calc(50% - 0.4375rem);
+  }
+
+  &:hover img {
+    opacity: 0.6;
+  }
+}
diff --git a/src/app/shared/components/download-button/download-button.component.ts b/src/app/shared/components/download-button/download-button.component.ts
new file mode 100644
index 00000000..cbd72c08
--- /dev/null
+++ b/src/app/shared/components/download-button/download-button.component.ts
@@ -0,0 +1,77 @@
+import { Component, HostListener, Input, OnInit } from '@angular/core';
+import { geosource } from '../../../../i18n/traductions';
+
+@Component({
+  selector: 'app-download-button',
+  templateUrl: './download-button.component.html',
+  styleUrls: ['./download-button.component.scss'],
+})
+export class DownloadButtonComponent implements OnInit {
+
+  loading: boolean = false;
+  @Input() url: string;
+  @Input() fileName: string;
+
+  // Event fired when one tab is closed or refreshed. We abort the on-going request
+  @HostListener('window:beforeunload')
+  abortCurrentRequest() {
+    this.controller.abort();
+  }
+
+  // Variable to manage the icon display for each resource. Possible values:
+  // - 'loading': display the spinner
+  // - 'cancel': display the cross to abort the on-going request
+  // -  null: display the download icon
+  signal: AbortSignal;
+  controller: AbortController;
+  timeOut: number;
+
+  abortMessage: string = geosource.downloads.abort;
+  downloadMessage: string = geosource.downloads.download;
+
+  constructor() { }
+
+  ngOnInit() {
+    this.controller = new AbortController();
+    this.signal = this.controller.signal;
+  }
+
+  download() {
+    this.loading = true;
+
+    fetch(this.url, { signal: this.signal }).then((response) => {
+      // To allow the download, get the repsons as a blob
+      return response.blob();
+    })
+      .then((blob) => {
+        // Create a temporary link and click on it to launch the blob download
+        const url = window.URL.createObjectURL(blob);
+        const a = document.createElement('a');
+        a.href = url;
+        a.rel = 'noopener';
+        a.download = this.fileName;
+        document.body.appendChild(a); // append the element to the dom -> otherwise it will not work in firefox
+        a.click();
+        a.remove();
+        this.loading = false;
+        clearTimeout(this.timeOut); // If the download is under 2000ms, we clear the ongoing timeout
+      })
+      .catch((err) => {
+        console.log('The fetch could not succeeded', err.message);
+      });
+  }
+
+  abortDownload() {
+
+    this.controller.abort();
+    this.loading = false;
+
+    // Create a new for a new DOM Request
+    this.controller = new AbortController();
+    this.signal = this.controller.signal;
+  }
+
+  ngOnDestroy() {
+    this.controller.abort();
+  }
+}
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
index 44210900..978ad89b 100644
--- a/src/app/shared/components/index.ts
+++ b/src/app/shared/components/index.ts
@@ -1,3 +1,4 @@
+import { DownloadButtonComponent } from '././download-button/download-button.component';
 import { BasicTabsComponent } from './basic-tabs/basic-tabs.component';
 import { CguModalComponent } from './cgu-modal/cgu-modal.component';
 import { FormatIconComponent } from './format-icon/format-icon.component';
@@ -8,7 +9,7 @@ import { PaginatorComponent } from './paginator/paginator.component';
 import { RestrictedAccessBannerComponent } from './restricted-access-banner/restricted-access-banner.component';
 
 // tslint:disable-next-line: max-line-length
-export { PaginatorComponent, InputClipboardComponent, FormatIconComponent, LinkCopyIconComponent, PageHeaderComponent, BasicTabsComponent, CguModalComponent, RestrictedAccessBannerComponent };
+export { PaginatorComponent, InputClipboardComponent, FormatIconComponent, DownloadButtonComponent, LinkCopyIconComponent, PageHeaderComponent, BasicTabsComponent, CguModalComponent, RestrictedAccessBannerComponent };
 
 // tslint:disable-next-line:variable-name
 export const SharedComponents = [
@@ -20,4 +21,5 @@ export const SharedComponents = [
   RestrictedAccessBannerComponent,
   CguModalComponent,
   FormatIconComponent,
+  DownloadButtonComponent,
 ];
-- 
GitLab