ojuso-map/apps/files/static/files/upload.js
Anna Sidwell 5cc0b7cdc1 Use latest form text
* Take the opportunity to reformat the source to not have so many hugely long lines.
* Put a few things through gettext that weren't going through it before
* Move 'Hold down Ctrl to select multiple files at once.' text to widget, out of
  help text for each individual control.
2019-04-07 16:08:09 +01:00

285 lines
8.3 KiB
JavaScript

class MultipleFilesWidget {
constructor(div, csrf_token) {
this.root = div
this.fieldName = this.root.getAttribute('data-field')
this.fileList = []
this.element = {
list: this.root.querySelector('.filewidget--list'),
fileInput: this.root.querySelector('.filewidget--input'),
field: document.querySelector(`[name="${this.fieldName}"]`)
}
this.csrf_token = csrf_token
this.status = {
IN_PROGRESS: 1,
DONE: 2,
}
this.endpoint = this.element.fileInput.getAttribute('data-url')
// Set up event listener for file upload button
this.element.fileInput.addEventListener('change', evt => {
const fileList = this.element.fileInput.files
for (let file of fileList) {
this.startUpload(file)
}
})
// If there is something in the field, we need to restore our state
if (this.element.field.value) {
this.rebuildListFromField()
}
// Set up listening for restore
this.element.field.addEventListener('change', evt => {
this.rebuildListFromField()
})
}
// file is a JS File object
startUpload(file) {
const fileName = file.name
let data = new FormData()
data.append('file', file)
fetch(this.endpoint, {
method: 'POST',
body: data,
headers: {
"X-CSRFToken": this.csrf_token,
},
})
.then(response => response.json())
.then(response => {
if (response.is_valid) {
response.originalName = fileName
this.fileFinished(response)
} else {
this.fileFailed(fileName, response.errors.toString())
}
})
.catch(err => {
this.fileFailed(fileName, err.toString())
})
this.addFile(null, file.name, this.status.IN_PROGRESS)
}
rebuildListFromField() {
const fieldContents = this.element.field.value
// Nothing to do
if (fieldContents === '') {
return;
}
try {
let list = JSON.parse(fieldContents)
for (let file of list) {
this.addFile(file.id, file.name, this.status.DONE)
}
} catch (e) {
// Not JSON, let's parse as CSV
for (let id of fieldContents.split(",")) {
this.addFile(id, `Saved upload (id ${id})`, this.status.DONE)
}
}
this.viewFileList()
}
addFile(id, name, state) {
let li = document.createElement('li')
li.className = 'filewidget--file'
li.innerHTML =
`<i class='filewidget--file--icon'></i> <span class='filewidget--file--name'></span>
<span class='filewidget--file--actions'></span>`
let file = {
root: li,
element: {
icon: li.querySelector('.filewidget--file--icon'),
name: li.querySelector('.filewidget--file--name'),
actions: li.querySelector('.filewidget--file--actions'),
},
name: name,
id: id || null,
}
switch (state) {
case this.status.DONE:
this.setFileDone(file)
break
case this.status.IN_PROGRESS:
this.setFileInProgress(file)
break
}
this.fileList.push(file)
this.viewFileList()
}
removeFile(file) {
// Remove file from display
this.element.list.removeChild(file.root)
// Remove file from fileList
this.fileList = this.fileList.filter(cmpFile => cmpFile === file)
}
fileFinished(response) {
let file = this.fileList.filter(file => file.name === response.originalName)[0]
// Set the ID
file.id = response.id
file.name = response.name
// Set the file state now it's finished
this.setFileDone(file)
this.viewFileList()
// Do this last tho
this.updateFormField()
}
fileFailed(fileName, errors) {
console.error('File upload failed!')
console.error(errors)
// Using data, find the file
let file = this.fileList.filter(file => file.name === fileName)[0]
// Set the file state now it's finished
this.setFileFailed(file)
this.viewFileList()
}
//
// Update the field that keeps track of file IDs
//
updateFormField() {
let oldVal = this.element.field.value
let fileList = this.fileList.filter(f => f.id != null)
.map(f => ({
id: f.id,
name: f.name
}))
this.element.field.value = JSON.stringify(fileList)
// Mark form as dirty
if (this.element.field.value !== oldVal) {
document.getElementById('case-study-form').dispatchEvent(new Event('dirty'))
}
}
//
// Manage the state of individual files in the widget
//
setFileState(file, state) {
file.element.icon.classList = `filewidget--file--icon ${state.iconClassList}`
file.element.name.innerText = state.fileName
file.element.actions.innerHTML = state.actions || ''
}
setFileFailed(file) {
this.setFileState(file, {
iconClassList: 'fa fa-exclamation-triangle',
fileName: `FAILED: '${file.name}'`
})
}
setFileInProgress(file) {
this.setFileState(file, {
iconClassList: 'fa fa-spinner fa-spin',
fileName: `${file.name} (uploading...)`
})
}
setFileDeletingInProgress(file) {
this.setFileState(file, {
iconClassList: 'fa fa-spinner fa-spin',
fileName: `Deleting...`
})
}
setFileDeletingFailed(file) {
this.setFileState(file, {
iconClassList: 'fa fa-exclamation-triangle',
fileName: `${file.name} (delete failed!)`
})
}
setFileDone(file) {
this.setFileState(file, {
iconClassList: 'fa fa-file-o',
fileName: `${file.name}`,
actions: '<a title="Remove"><i class="fa fa-times"></i></a>'
})
file.element.actions.querySelector('a').addEventListener('click', () => {
this.setFileDeletingInProgress(file)
return fetch(`/files/delete/${file.id}/`, {
method: 'POST',
credentials: "same-origin",
headers: {
"X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(),
}
}).then(response => {
if (response.ok) {
this.removeFile(file)
} else {
this.setFileDeletingFailed(file)
}
})
})
}
//
// Redraw the file list
//
viewFileList() {
for (let file of this.fileList) {
// Check if it's appended to the list
if (!this.element.list.contains(file.root)) {
this.element.list.appendChild(file.root)
}
}
}
}
$(function() {
// Display OS-specific stuff
function os_class() {
if (navigator.platform.indexOf("Win") != -1) return "os-windows";
if (navigator.platform.indexOf("Mac") != -1) return "os-mac";
return "os-other";
}
document.querySelector('body').classList.add(os_class())
// Get the CSRF token for form submitting
let csrf_token = document.querySelector("[name=csrfmiddlewaretoken]").value
window.images = new MultipleFilesWidget(
document.querySelector('[data-field=images_files]'),
csrf_token,
)
window.official_project_documents = new MultipleFilesWidget(
document.querySelector('[data-field=official_project_documents_files]'),
csrf_token,
)
window.other_documents = new MultipleFilesWidget(
document.querySelector('[data-field=other_documents_files]'),
csrf_token,
)
window.shapefiles = new MultipleFilesWidget(
document.querySelector('[data-field=shapefiles_files]'),
csrf_token,
)
})