2016-04-07 15:23:42 +01:00

510 lines
12 KiB
HTML
Executable File

<!--
@license
Copyright (c) 2015 Winston Howes. All rights reserved.
This code may only be used under the MIT license found at https://github.com/winhowes/file-upload/blob/master/LICENSE
-->
<link rel="import" href="../../bower_components/polymer/polymer.html">
<!--
An element providing a solution to no problem in particular.
Example:
<file-upload target="/path/to/destination"></file-upload>
@demo
-->
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/paper-progress/paper-progress.html">
<link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
<dom-module id="file-upload">
<style type="text/css">
:host {
display: inline-block;
}
.enabled {
border: 1px dashed #555;
@apply(--file-upload-upload-border-enabled);
}
.hover {
opacity: .7;
border: 1px dashed #111;
@apply(--file-upload-upload-border-hover);
}
#UploadBorder{
vertical-align: middle;
color: #555;
padding: 20px;
max-height: 300px;
overflow-y: auto;
display: inline-block;
@apply(--file-upload-upload-border);
}
#dropArea {
text-align: center;
@apply(--file-upload-drop-area);
}
paper-button#button {
margin-bottom: 20px;
@apply(--file-upload-button);
}
.file {
padding: 10px 0px;
@apply(--file-upload-file);
}
.commands {
float: right;
@apply(--file-upload-commands);
}
.commands iron-icon:not([icon="check-circle"]) {
cursor: pointer;
opacity: .9;
@apply(--file-upload-commands-faded);
}
.commands iron-icon:hover {
opacity: 1;
@apply(--file-upload-commands-hovered);
}
[hidden] {
display: none;
}
.error {
color: #f40303;
font-size: 11px;
margin: 2px 0px -3px;
@apply(--file-upload-error);
}
paper-progress {
--paper-progress-active-color: #03a9f4;
}
paper-progress[error] {
--paper-progress-active-color: #f40303;
}
paper-button#button {
margin-bottom: 20px;
background-color: aquamarine;
}
</style>
<template>
<div>
<paper-button id="button" icon="file-upload" class="blue" on-click="_fileClick">
<content></content>
</paper-button>
<div id='UploadBorder'>
<div id="dropArea" hidden$="{{!_shownDropText}}">{{droptext}}</div>
<template is="dom-repeat" items="{{files}}">
<div class="file">
<div class="name">
<span>{{item.name}}</span>
<div class="commands">
<iron-icon icon="autorenew" title="{{retryText}}" on-click="_retryUpload" hidden$="{{!item.error}}"></iron-icon>
<iron-icon icon="cancel" title="{{removeText}}" on-click="_cancelUpload" hidden$="{{item.complete}}"></iron-icon>
<iron-icon icon="check-circle" title="{{successText}}" hidden$="{{!item.complete}}"></iron-icon>
</div>
</div>
<div class="error" hidden$="{{!item.error}}">{{errorText}}</div>
<div hidden$={{progressHidden}}>
<paper-progress value$="{{item.progress}}" error$="{{item.error}}"></paper-progress>
</div>
</div>
</template>
</div>
</div>
<input type="file" id="fileInput" on-change="_fileChange" hidden multiple="{{multi}}" accept="{{accept}}">
<!--<paper-toast id="toastSuccess" text="File uploaded successfully!"></paper-toast>
<paper-toast id="toastFail" text="Error uploading file!"></paper-toast>-->
</template>
</dom-module>
<script>
Polymer({
is: 'file-upload',
/**
* Fired when a response is received status code 200.
*
* @event success
*/
/**
* Fired when a response is received other status code.
*
* @event error
*/
/**
* Fired when a file is about to be uploaded.
*
* @event before-upload
*/
properties: {
/**
* `target` is the target url to upload the files to.
* Additionally by adding "<name>" in your url, it will be replaced by
* the file name.
*/
target: {
type: String,
value: ""
},
/**
* `accept` is the set of comma separated file extensions or mime types
* to filter as accepted.
*/
accept: {
type: String,
value: ""
},
/**
* `progressHidden` indicates whether or not the progress bar should be hidden.
*/
progressHidden: {
type: Boolean,
value: false
},
/**
* `droppable` indicates whether or not to allow file drop.
*/
droppable: {
type: Boolean,
value: false,
observer: 'init'
},
/**
* `droptext` is the text to display in the file drop area.
*/
droptext: {
type: String,
value: "Drop Files Here"
},
/**
* `multi` indicates whether or not to allow multiple files to be uploaded.
*/
multi: {
type: Boolean,
value: false
},
/**
* `files` is the list of files to be uploaded
*/
files: {
type: Array,
value: function() {
return [];
}
},
/**
* `method` is the http method to be used during upload
*/
method: {
type: String,
value: "POST"
},
/**
* `raised` indicates whether or not the button should be raised
*/
raised: {
type: Boolean,
value: false
},
/**
* `noink` indicates that the button should not have an ink effect
*/
noink: {
type: Boolean,
value: false
},
/**
* `headers` is a key value map of header names and values
*/
headers: {
type: Object,
value: {},
},
/**
* `retryText` is the text for the tooltip to retry an upload
*/
retryText: {
type: String,
value: 'Retry Upload'
},
/**
* `removeText` is the text for the tooltip to remove an upload
*/
removeText: {
type: String,
value: 'Remove'
},
/**
* `successText` is the text for the tooltip of a successful upload
*/
successText: {
type: String,
value: 'Success'
},
/**
* `errorText` is the text to display for a failed upload
*/
errorText: {
type: String,
value: 'Error uploading file...'
},
/**
* `_shownDropText` indicates whether or not the drop text should be shown
*/
_shownDropText: {
type: Boolean,
value: false
},
site: {
type: String,
value: 'swsdp'
},
container: {
type: String,
value: 'documentLibrary'
},
},
/**
* Clears the list of files
*/
clear: function() {
this.set("files", []);
this._showDropText();
},
ready: function() {
},
init: function() {
if (this.raised) {
this.toggleAttribute("raised", true, this.$.button);
}
if (this.noink) {
this.toggleAttribute("noink", true, this.$.button);
}
if (this.droppable === 'true') {
this._showDropText();
this.setupDrop();
} else { this._hideDropText(); }
},
/**
* A function to set up a drop area for drag-and-drop file uploads
*/
setupDrop: function() {
var uploadBorder = this.$.UploadBorder;
this.toggleClass("enabled", true, uploadBorder);
this.ondragover = function(e) {
e.stopPropagation();
this.toggleClass("hover", true, uploadBorder);
return false;
}
this.ondragleave = function() {
this.toggleClass("hover", false, uploadBorder);
return false;
}
this.ondrop = function(event) {
this.toggleClass("hover", false, uploadBorder);
event.preventDefault();
var length = event.dataTransfer.files.length;
for (var i = 0; i < length; i++) {
var file = event.dataTransfer.files[i];
// Check if multiple upload is allowed
if (!this.multi && this.files.length !== 0) {
return;
}
// Check if filetype is accepted
var mimeType = ((file.type !== '') ? file.type.match(/^[^\/]*\//)[0] : null);
var fileType = file.name.match(/\.[^\.]*$/)[0];
if (this.accept !== '' && !(this.accept.indexOf(mimeType) > -1 || this.accept.indexOf(fileType) > -1)) {
return;
}
file.progress = 0;
file.error = false;
file.complete = false;
this.push("files", file);
this.uploadFile(file);
}
}
},
/**
* Clicks the invisible file input
*/
_fileClick: function() {
var elem = this.$.fileInput;
if (elem && document.createEvent) { // sanity check
var evt = document.createEvent("MouseEvents");
evt.initEvent("click", true, false);
elem.dispatchEvent(evt);
}
},
/**
* Called whenever the list of selected files changes
*/
_fileChange: function(e) {
var length = e.target.files.length;
for (var i = 0; i < length; i++) {
var file = e.target.files[i];
file.progress = 0;
file.error = false;
file.complete = false;
this.push("files", file);
this.uploadFile(file);
}
},
/**
* Cancels the file upload for a specific file
*
* @param {object} a file, an element of the files array
*/
cancel: function(file) {
if (file && file.xhr) {
file.xhr.abort();
this.splice("files", this.files.indexOf(file), 1);
this._showDropText();
}
},
/**
* Cancels the file upload
*
* @param {object}, an event object
*/
_cancelUpload: function(e) {
this.cancel(e.model.__data__.item);
},
/**
* Retries to upload the file
*
* @param {object}, an event object
*/
_retryUpload: function(e) {
e.model.set("item.error", false);
e.model.set("item.progress", 0);
// The async helps give visual feedback of a retry occurring, even though it's less efficient.
var self = this;
this.async(function() {
self.uploadFile(e.model.__data__.item);
}, 50);
},
/**
* Whether or not to display the drop text
*/
_showDropText: function() {
this.set("_shownDropText", true);
},
_hideDropText: function() {
this.set("_shownDropText", false);
var uploadBorder = this.$.UploadBorder;
this.toggleClass("enabled", false, uploadBorder);
this.toggleClass("hover", false, uploadBorder);
},
/**
* Uploads a file
*
* @param {object} a file, an element of the files array
*/
uploadFile: function(file) {
if (!file) {
return;
}
this.fire('before-upload');
this._showDropText();
var prefix = "files." + this.files.indexOf(file);
var self = this;
var formData = new FormData();
formData.append("filedata", file, file.name);
formData.append('siteid', this.site);
formData.append('containerid', this.container);
var xhr = file.xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
var done = e.loaded, total = e.total;
self.set(prefix + ".progress", Math.floor((done/total)*1000)/10);
};
var url = this.target.replace("<name>", file.name);
xhr.open(this.method, url, true);
for (key in this.headers) {
if (this.headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, this.headers[key]);
}
}
xhr.setRequestHeader("Authorization", 'Basic ' + btoa('admin:admin'))
xhr.onload = function(e) {
if (xhr.status === 200) {
self.fire("success", {xhr: xhr});
self.set(prefix + ".complete", true);
} else {
self.set(prefix + ".error", true);
self.set(prefix + ".complete", false);
self.set(prefix + ".progress", 100);
self.updateStyles();
self.fire("error", {xhr:xhr});
}
};
xhr.send(formData);
}
});
</script>