/* --- name: DropZone.HTML5 description: A DropZone module. Handles uploading using the HTML5 method license: MIT-style license authors: - Mateusz Cyrankiewicz - Juan Lago requires: [DropZone] provides: [DropZone.HTML5] ... */ DropZone.HTML5 = new Class({ Extends: DropZone, initialize: function (options) { this.setOptions(options); this.method = 'HTML5' this.activate(); }, bound: {}, activate: function (){ this.parent(); // If drop area is specified, // and in HTML5 mode, // activate dropping if(this.uiDropArea){ // Extend new events Object.append(Element.NativeEvents, { dragenter: 2, dragleave: 2, dragover: 2, drop: 2 }); this.uiDropArea.addEvents({ 'dragenter': function (e) { e.stop(); this.uiDropArea.addClass('dropzone_over'); }.bind(this), 'dragleave': function (e) { e.stop(); if (e.target && e.target === this.uiDropArea) { this.uiDropArea.removeClass('dropzone_over'); } }.bind(this), 'dragover': function (e) { e.stop(); e.preventDefault(); }.bind(this), 'drop': function (e) { e.stop(); if(e.event.dataTransfer) { this.addFiles(e.event.dataTransfer.files); } this.uiDropArea.removeClass('dropzone_over'); }.bind(this) }); // prevent defaults on window this.bound = { stopEvent: this._stopEvent.bind(this) } $(document.body).addEvents({ 'dragenter': this.bound.stopEvent, 'dragleave': this.bound.stopEvent, 'dragover': this.bound.stopEvent, 'drop': this.bound.stopEvent }); } // Activate trigger for html file input this._activateHTMLButton(); }, upload: function (){ this.fileList.each(function(file, i){ if (file.checked && !file.uploading && this.nCurrentUploads < this.options.max_queue) { // Upload only checked and new files file.uploading = true; this.nCurrentUploads++; this._html5Send(file, 0, false); } }, this); this.parent(); }, _html5Send: function (file, start, resume) { var item; //if (this.uiList) item = this.uiList.getElement('#dropzone_item_' + (file.uniqueid)); // now getting the item globally in case it was moved somewhere else in onItemAdded event // this way it can always remain controlled item = $('dropzone_item_' + file.uniqueid + '_' + file.id); var end = this.options.block_size, chunk, is_blob = true; var total = start + end; if (total > file.size) end = total - file.size; // Get slice method if (file.file.slice) { // Standard browsers chunk = file.file.slice(start, total); } else if (file.file.mozSlice) { // Mozilla based chunk = file.file.mozSlice(start, total); } else if (file.file.webkitSlice && !Browser.safari) {// Chrome 20- and webkit based // Safari slices the file badly chunk = file.file.webkitSlice(start, total); } else { // Safari 5- // send as form data instead of Blob chunk = new FormData(); chunk.append('file', file.file); is_blob = false; } // Set headers var headers = { 'Cache-Control': 'no-cache' } // Add call-specific vars var url = this.url + '&' + Object.toQueryString({ 'X-Requested-With': 'XMLHttpRequest', 'X-File-Name': file.name, 'X-File-Size': file.size, 'X-File-Id': file.id, 'X-File-Resume': resume }); // Send request var xhr = new Request.Blob({ url: url, headers: headers, trackProgress:true, onProgress: function(e){ if(!is_blob){ // track xhr progress only if data isn't actually sent as a chunk (eg. in Safari) var perc = e.loaded / e.total * 100; this.fileList[file.id].progress = perc; this._itemProgress(item, perc); } }.bind(this), onSuccess: function (response) { try { response = JSON.decode(response, true); } catch(e){ response = ''; } if(typeof this.fileList[file.id] != 'undefined' && !this.fileList[file.id].cancelled){ if (this._checkResponse(response)) { if (response.finish == true) { // || total >= file.size // sometimes the size is measured wrong and fires too early? // job done! this._itemComplete(item, file, response); if (this.nCurrentUploads != 0 && this.nCurrentUploads < this.options.max_queue && file.checked) this.upload(); } else { // in progress.. if(file.checked) { var perc = (total / file.size) * 100; // it's used to calculate global progress this.fileList[file.id].progress = perc; this._itemProgress(item, perc); this._html5Send(file, start + response.size.toInt(), true) // Recursive upload } } } else { // response errror! this._itemError(item, file, response); } } else { // item doesn't exist anymore, probably cancelled } }.bind(this), onFailure: function(){ this._itemError(item, file); }.bind(this) }); xhr.send(chunk); }, cancel: function (id, item) { this.parent(id, item); // }, kill: function(){ this.parent(); // remove events if(this.uiDropArea) $(document.body).removeEvents({ 'dragenter': this.bound.stopEvent, 'dragleave': this.bound.stopEvent, 'dragover': this.bound.stopEvent, 'drop': this.bound.stopEvent }); }, /* Private methods */ _newInput: function (){ this.parent(); // add interaction to input this.lastInput.addEvent('change', function (e) { e.stop(); this.addFiles(this.lastInput.files); }.bind(this)); }, _itemError: function(item, file, response){ this.parent(item, file, response); if(this.nCurrentUploads == 0) this._queueComplete(); else if (this.nCurrentUploads != 0 && this.nCurrentUploads < this.options.max_queue) this.upload(); }, _stopEvent: function(e){ e.stop(); } });