├── LICENSE.md ├── .gitignore ├── screenshot.png ├── README.md ├── upload.php ├── index.html ├── style.css └── script.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.esproj 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbstjn/DropFile.php/HEAD/screenshot.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DropFile.php 2 | 3 | A quick and dirty file uploader. With a full page dropzone for easy Drag'n'Drop file uploads. Backend is handled by a PHP script. 4 | 5 | ![DropFile.php screenshot](http://cdn.sbstjn.com/2014/10/screenshot.9db9e10f.png) 6 | 7 | # Security Advise 8 | 9 | Make sure you wrap an authentication around DropFile.php interface! 10 | 11 | # License 12 | 13 | DropFile.php is licensed under MIT License -------------------------------------------------------------------------------- /upload.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DropFile Box 5 | 6 | 7 | 8 |

Drop files here

9 |
10 |
    11 |
    12 |
    13 | 14 | 15 | 25 | 26 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html, body, p, h1, h2, div, ul, li, pre { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | #zone { 7 | width: 100%; 8 | min-height: 100%; 9 | background-color: tomato; 10 | text-align: center; 11 | } 12 | 13 | #zone h1 { 14 | font-family: sans-serif; 15 | font-weight: 300; 16 | color: white; 17 | font-size: 52px; 18 | display: inline-block; 19 | margin: 0 auto; 20 | padding: 60px 8px 0 8px; 21 | } 22 | 23 | #dragResults { 24 | padding: 15px 0 40px 0; 25 | width: 100%; 26 | } 27 | 28 | #dragResultsContent { 29 | width: 420px; 30 | margin: 0 auto; 31 | background-color: white; 32 | border-radius: 3px; 33 | padding-bottom: 6px; 34 | } 35 | 36 | #files { 37 | list-style-type: none; 38 | } 39 | 40 | #files li { 41 | padding: 2px 8px; 42 | } 43 | 44 | .options { 45 | list-style-type: none; 46 | float: right; 47 | opacity: 0; 48 | transition: all 0.4s ease; 49 | } 50 | 51 | .options.show { 52 | opacity: 1; 53 | } 54 | 55 | .options li { 56 | display: inline-block; 57 | padding: 0 6px; 58 | float: left; 59 | text-align: center; 60 | font-family: sans-serif; 61 | font-size: 12px; 62 | line-height: 24px; 63 | } 64 | 65 | .size { 66 | font-family: sans-serif; 67 | line-height: 24px; 68 | font-size: 12px; 69 | float: left; 70 | position: relative; 71 | top: 2px; 72 | padding-left: 10px; 73 | color: rgba(51, 51, 51, 0.3); 74 | } 75 | 76 | .filename { 77 | font-family: sans-serif; 78 | line-height: 24px; 79 | font-size: 12px; 80 | float: left; 81 | position: relative; 82 | top: 2px; 83 | padding-left: 10px; 84 | } 85 | 86 | .options li a { 87 | text-decoration: none; 88 | text-transform: lowercase; 89 | color: rgba(100, 100, 100, 0.5); 90 | transition: all 0.2s ease; 91 | } 92 | 93 | .options li a.active { 94 | color: tomato; 95 | } 96 | 97 | #result { 98 | position: relative; 99 | top: 0; 100 | left: 0; 101 | z-index: 10; 102 | } 103 | 104 | .progress { 105 | clear: both; 106 | background-color: RGBA(255, 99, 71, 0.4); 107 | padding: 2px 4px 3px 10px; 108 | overflow: scroll; 109 | line-height: 28px; 110 | border-radius: 3px; 111 | color: rgba(51, 51, 51, 0.4); 112 | box-sizing: border-box; 113 | height: 32px; 114 | transition: all 0.2s linear; 115 | font-family: sans-serif; 116 | line-height: 28px; 117 | font-size: 12px; 118 | } 119 | 120 | .progress.done { 121 | background-color: rgba(51, 51, 51, 0.1); 122 | } 123 | 124 | .url { 125 | clear: both; 126 | background-color: rgba(51, 51, 51, 0.1); 127 | padding: 2px 4px 3px 10px; 128 | overflow: scroll; 129 | line-height: 28px; 130 | border-radius: 3px; 131 | box-sizing: border-box; 132 | height: 32px; 133 | } -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | function safe_tags_replace(str) { 2 | /** @see http://stackoverflow.com/a/5499821 **/ 3 | var tagsToReplace = {'&': '&', '<': '<', '>': '>'}; 4 | return str.replace(/[&<>]/g, function replaceTag(tag) { 5 | return tagsToReplace[tag] || tag; 6 | }); 7 | } 8 | 9 | var Uploader = function(data) { 10 | this.options = data; 11 | this.list = {}; 12 | this.zone = document.getElementById(this.options.zone); 13 | this.result = document.getElementById(this.options.result); 14 | this.template = [ 15 | '
  • ', 16 | '{localname}', 17 | '{size} kb', 18 | '
    ', 23 | '
    ', 24 | '
  • ' 25 | ].join(''); 26 | 27 | this.formater = { 28 | 'url': '{url}', 29 | 'markdown': '[{name}]({url})', 30 | 'html': '{name}' 31 | }; 32 | 33 | this.init(); 34 | }; 35 | 36 | Uploader.prototype.init = function() { 37 | this.zone.addEventListener('dragover', this.handleDrag.bind(this), false); 38 | this.zone.addEventListener('drop', this.handleDrop.bind(this), false); 39 | 40 | this.result.addEventListener('click', this.handleSwitch.bind(this)); 41 | }; 42 | 43 | Uploader.prototype.handleSwitch = function(e) { 44 | if (e.toElement.nodeName == 'A') { 45 | this.handleSwitch(e); 46 | } 47 | }; 48 | 49 | Uploader.prototype.switchDisplay = function(id, type, url) { 50 | var query = 'li[data-id="' + id + '"]'; 51 | var value = safe_tags_replace(this.formater[type] 52 | .replace('{url}', url) 53 | .replace('{name}', this.result.querySelector(query).getAttribute('data-name'))); 54 | 55 | var options = this.result.querySelectorAll(query + ' .option'); 56 | for (var i = 0, m = options.length; i < m; i++) { 57 | options[i].className = 'option'; 58 | } 59 | 60 | this.result.querySelector(query + ' .option[data-type="' + type + '"]').className = 'option active'; 61 | this.result.querySelector(query + ' pre').innerHTML = value; 62 | } 63 | 64 | Uploader.prototype.handleSwitch = function(e) { 65 | e.stopPropagation(); 66 | e.preventDefault(); 67 | 68 | var id = e.path[3].getAttribute('data-id'); 69 | var type = e.srcElement.getAttribute('data-type'); 70 | var url = e.path[3].getAttribute('data-url'); 71 | 72 | this.switchDisplay(id, type, url); 73 | }; 74 | 75 | Uploader.prototype.handleDrag = function(e) { 76 | e.stopPropagation(); 77 | e.preventDefault(); 78 | e.dataTransfer.dropEffect = 'move'; 79 | }; 80 | 81 | Uploader.prototype.handleDrop = function(e) { 82 | e.stopPropagation(); 83 | e.preventDefault(); 84 | 85 | this.preview(e); 86 | }; 87 | 88 | Uploader.prototype.preview = function(e) { 89 | var files = e.dataTransfer.files; 90 | var output = []; 91 | 92 | for (var i = 0, f; f = files[i]; i++) { 93 | f.id = (Math.random().toString(36)+'00000000000000000').slice(2, 8);; 94 | this.list[f.id] = f; 95 | 96 | output.push((this.template + '') 97 | .replace('{localname}', escape(f.name)) 98 | .replace('{labelname}', escape(f.name)) 99 | .replace('{size}', (Math.round(f.size*100/1024)/100)) 100 | .replace('{id}', f.id)); 101 | } 102 | 103 | this.result.innerHTML += output.join(''); 104 | this.upload(); 105 | }; 106 | 107 | Uploader.prototype.upload = function() { 108 | for (var n in this.list) { 109 | this.uploadFile(this.list[n]); 110 | } 111 | }; 112 | 113 | Uploader.prototype.uploadProgress = function(e, i) { 114 | var percent = Math.round(e.loaded * 100 / e.total); 115 | var element = this.result.querySelector('li[data-id="' + i + '"] .progress'); 116 | 117 | if (element) { 118 | element.style.width = percent + '%'; 119 | } 120 | }; 121 | 122 | Uploader.prototype.uploadFinish = function(e, i) { 123 | var body = JSON.parse(e.srcElement.responseText); 124 | var query = 'li[data-id="' + i + '"]'; 125 | var progress = this.result.querySelector(query + ' .progress'); 126 | 127 | if (progress) { 128 | progress.className = 'progress done'; 129 | setTimeout(function() { 130 | var url = this.options.baseurl + body.file; 131 | var data = '
    ' + url + '
    '; 132 | this.result.querySelector(query + ' .options').className = 'options show'; 133 | this.result.querySelector(query).setAttribute('data-url', url); 134 | this.result.querySelector(query + ' .response').innerHTML = data; 135 | }.bind(this), 200); 136 | } 137 | }; 138 | 139 | Uploader.prototype.uploadFile = function(file) { 140 | var xhr = new XMLHttpRequest(); 141 | var fd = new FormData(); 142 | 143 | fd.append('file', file); 144 | 145 | xhr.upload.addEventListener("progress", function(e) { 146 | this.uploadProgress(e, file.id); 147 | }.bind(this), false); 148 | 149 | xhr.addEventListener("load", function(e) { 150 | this.uploadFinish(e, file.id); 151 | }.bind(this), false); 152 | 153 | xhr.open("POST", this.options.upload); 154 | xhr.send(fd); 155 | }; --------------------------------------------------------------------------------