Ink.createModule('Ink.UI.Upload', '1', [ 'Ink.Dom.Event_1', 'Ink.Dom.Element_1', 'Ink.Dom.Browser_1', 'Ink.UI.Common_1' ], function(Event, Element, Browser, Common) { 'use strict'; var DirectoryReader = function(options) { this.init(options); }; DirectoryReader.prototype = { init: function(options) { this._options = Ink.extendObj({ entry: undefined, maxDepth: 10 }, options || {}); try { this._read(); } catch(e) { Ink.error(e); } }, _read: function() { if(!this._options.entry) { Ink.error('You must specify the entry!'); return; } try { this._readDirectories(); } catch(e) { Ink.error(e); } }, _readDirectories: function() { var entries = [], running = false, maxDepth = 0; /* TODO return as tree because much better well */ var _readEntries = Ink.bind(function(currentEntry) { var dir = currentEntry.createReader(); running = true; dir.readEntries(Ink.bind(function(res) { if(res.length > 0) { for(var i = 0, len = res.length; i=0; i--) { if(typeof(arr[i]) === 'undefined' || arr[i] === null || arr[i] === '') { arr.splice(i, 1); } } return arr; } }; var Queue = { lists: [], items: [], /** * Create new queue list * @function create * @public * @param {String} list name * @param {Function} function to iterate on items * @return {Object} list id */ create: function(name) { var id; name = String(name); this.lists.push({name: name}); id = this.lists.length - 1; return id; }, getItems: function(parentId) { if(!parentId) { return this.items; } var items = []; for(var i = 0, len = this.items.length; i=0; i--) { if(this.items[i] && id === this.items[i].parentId) { this.remove(this.items[i].parentId, this.items[i].pid); } } if(!keepList) { this.lists.splice(id, 1); } return true; } catch(e) { Ink.error('Purge: invalid id'); return false; } }, /** * add an item to a list * @function add * @public * @param {String} name * @param {Object} item * @return {Number} pid */ add: function(parentId, item, priority) { if(!this.lists[parentId]) { return false; } if(typeof(item) !== 'object') { item = String(item); } var pid = parseInt(Math.round(Math.random() * 100000) + "" + Math.round(Math.random() * 100000), 10); priority = priority || 0; this.items.push({parentId: parentId, item: item, priority: priority || 0, pid: pid}); return pid; }, /** * View list * @function view * @public * @param {Number} list id * @param {Number} process id * @return {Object} item */ view: function(parentId, pid) { var id = this._searchByPid(parentId, pid); if(id === false) { return false; } return this.items[id]; }, /** * Remove an item * @function remove * @public * @param {Object} item * @return {Object|Boolean} removed item or false if not found */ remove: function(parentId, pid) { try { var id = this._searchByPid(parentId, pid); if(id === false) { return false; } this.items.splice(id, 1); return true; } catch(e) { Ink.error('Remove: invalid id'); return false; } }, _searchByPid: function(parentId, pid) { if(!parentId && typeof(parentId) === 'boolean' || !pid) { return false; } parentId = parseInt(parentId, 10); pid = parseInt(pid, 10); if(isNaN(parentId) || isNaN(pid)) { return false; } for(var i = 0, len = this.items.length; i this._options.minSizeToUseChunks; }, _dropEventHandler: function(ev) { Event.stop(ev); this.publish('DropComplete', ev.dataTransfer); var data = ev.dataTransfer; if(!data || !data.files || !data.files.length) { return false; } this._files = data.files; this._files = Array.prototype.slice.call(this._files || [], 0); // check if webkitGetAsEntry exists on first item if(data.items && data.items[0] && data.items[0].webkitGetAsEntry) { if(!this._options.foldersEnabled) { return setTimeout(Ink.bind(this._addFilesToQueue, this, this._files), 0); } var entry, folders = []; for(var i = ev.dataTransfer.items.length-1; i>=0; i--) { entry = ev.dataTransfer.items[i].webkitGetAsEntry(); if(entry && entry.isDirectory) { folders.push(entry); this._files[i].isDirectory = true; this._files.splice(i, 1); } } // starting callback hell this._addFolderToQueue(folders, Ink.bind(function() { setTimeout(Ink.bind(this._addFilesToQueue, this, this._files), 0); }, this)); } else { setTimeout(Ink.bind(this._addFilesToQueue, this, this._files), 0); } return true; }, _addFolderToQueue: function(folders, cb) { var files = [], invalidFolders = {}; if(!folders || !folders.length) { cb(); return files; } var getFiles = function(entries) { var files = []; for(var i = 0, len = entries.length; i this._options.maxFilesize) { this.publish('MaxSizeFailure', file, this._options.maxFilesize); continue; } fileID = parseInt(Math.round(Math.random() * 100000) + "" + Math.round(Math.random() * 100000), 10); o = { id: i, data: file, fileID: fileID, directory: file.isDirectory }; Queue.add(this._queueId, o); this.publish('FileAddedToQueue', o); } this._processQueue(true); this._files = []; }, _processQueue: function(internalUpload) { if(this._queueRunning) { return false; } this.running = 0; var max = 1, i = 0, items, queueLen = Queue.items.length; this._queueRunning = true; this.interval = setInterval(Ink.bind(function() { if(Queue.items.length === i && this.running === 0) { Queue.purge(this._queueId, true); this._queueRunning = false; clearInterval(this.interval); this.publish('QueueEnd', this._queueId, queueLen); } items = Queue.getItems(this._queueId); if(this.running < max && items[i]) { if(!items[i].canceled) { _doRequest.call(this, items[i].pid, items[i].item.data, items[i].item.fileID, items[i].item.directory, internalUpload); this.running++; i++; } else { var j = i; while(items[j] && items[j].canceled) { i++; j++; } } return true; } return false; }, this), 100); var _doRequest = function(pid, data, fileID, directory, internalUpload) { var o = { file: data, fileID: fileID, cb: Ink.bind(function() { this.running--; }, this) }; if(internalUpload) { if(directory) { // do magic o.cb(); } else { this._upload(o); } } }; return true; }, _upload: function(o) { var file = o.file, xhr = new XMLHttpRequest(), fileID = o.fileID; this.publish('BeforeUpload', file, this._options.extraData, fileID, xhr, this._supportChunks(file.size)); var forceAbort = function(showError) { if(o.cb && typeof(o.cb === 'function')) { o.cb(); } this.publish('OnProgress', { length: file.size, lengthComputable: true, loaded: file.size, total: file.size }, file, fileID); this.publish('EndUpload', file, fileID, (showError ? { error: true } : true)); this.publish('InvalidFile', file, 'name'); xhr.abort(); }; if(this._options.INVALID_FILE_NAME && this._options.INVALID_FILE_NAME instanceof RegExp) { if(this._options.INVALID_FILE_NAME.test(o.file.name)) { forceAbort.call(this); return; } } // If file was renamed, abort it // FU OPERA: Opera always return lastModified date as null if(!file.lastModifiedDate && !Ink.Dom.Browser.OPERA) { forceAbort.call(this, true); return; } xhr.upload.onprogress = Ink.bind(this.publish, this, 'OnProgress', file, fileID); var endpoint, method; if(this._supportChunks(file.size)) { if(file.size <= file.chunk_offset) { endpoint = this._options.endpointChunkCommit; method = 'POST'; } else { endpoint = this._options.endpointChunk; if(file.chunk_upload_id) { endpoint += '?upload_id=' + file.chunk_upload_id; } if(file.chunk_offset) { endpoint += '&offset=' + file.chunk_offset; } method = 'PUT'; } } else { endpoint = this._options.endpoint; method = 'POST'; } xhr.open(method, endpoint, true); xhr.withCredentials = true; xhr.setRequestHeader("x-requested-with", "XMLHttpRequest"); if(this._supportChunks(file.size)) { xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); } var fd = new FormData(), blob; if("Blob" in window && typeof Blob === 'function') { blob = new Blob([file], { type: file.type }); if(this._supportChunks(file.size)) { file.chunk_offset = file.chunk_offset || 0; blob = blob.slice(file.chunk_offset, file.chunk_offset + this._options.chunkSize); } else { fd.append(this._options.fileFormName, blob, file.name); } } else { fd.append(this._options.fileFormName, file); } if(!this._supportChunks(file.size)) { for(var k in this._options.extraData) { if(this._options.extraData.hasOwnProperty(k)) { fd.append(k, this._options.extraData[k]); } } } else { fd.append('upload_id', file.chunk_upload_id); fd.append('path', file.upload_path); } if(!file.hasParent) { if(!this._supportChunks(file.size)) { xhr.send(fd); } else { if(file.size <= file.chunk_offset) { xhr.send('upload_id=' + file.chunk_upload_id + '&path=' + file.upload_path + '/' + file.name); } else { xhr.send(blob); } } } else { this.publish('cbCreateFolder', file.parentID, file.fullPath, this._options.extraData, this._folders, file.rootPath, Ink.bind(function() { if(!this._supportChunks(file.size)) { xhr.send(fd); } else { if(file.size <= file.chunk_offset) { xhr.send('upload_id=' + file.chunk_upload_id + '&path=' + file.upload_path + '/' + file.name); } else { xhr.send(blob); } } }, this)); } xhr.onload = Ink.bindEvent(function() { /* jshint boss:true */ if(this._supportChunks(file.size) && file.size > file.chunk_offset) { if(xhr.response) { var response = JSON.parse(xhr.response); // check expected offset var invalidOffset = file.chunk_offset && response.offset !== (file.chunk_offset + this._options.chunkSize) && file.size !== response.offset; if(invalidOffset) { if(o.cb) { o.cb(); } this.publish('ErrorUpload', file, fileID); } else { file.chunk_upload_id = response.upload_id; file.chunk_offset = response.offset; file.chunk_expires = response.expires; this._upload(o); } } else { if(o.cb) { o.cb(); } this.publish('ErrorUpload', file, fileID); } return (xhr = null); } if(o.cb) { o.cb(); } if(xhr.responseText && xhr['status'] < 400) { this.publish('EndUpload', file, fileID, xhr.responseText); } else { this.publish('ErrorUpload', file, fileID); } return (xhr = null); }, this); xhr.onerror = Ink.bindEvent(function() { if(o.cb) { o.cb(); } this.publish('ErrorUpload', file, fileID); }, this); xhr.onabort = Ink.bindEvent(function() { if(o.cb) { o.cb(); } this.publish('AbortUpload', file, fileID, { abortAll: Ink.bind(this.abortAll, this), abortOne: Ink.bind(this.abortOne, this) }); }, this); }, abortAll: function() { if(!this._queueRunning) { return false; } clearInterval(this.interval); this._queueRunning = false; Queue.purge(this._queueId, true); return true; }, abortOne: function(id, cb) { var items = Queue.getItems(0), o; for(var i = 0, len = items.length; i