/* angular-file-upload v1.1.5 https://github.com/nervgh/angular-file-upload */ (function(angular, factory) { if (typeof define === 'function' && define.amd) { define('angular-file-upload', ['angular'], function(angular) { return factory(angular); }); } else { return factory(angular); } }(typeof angular === 'undefined' ? null : angular, function(angular) { var module = angular.module('angularFileUpload', []); 'use strict'; /** * Classes * * FileUploader * FileUploader.FileLikeObject * FileUploader.FileItem * FileUploader.FileDirective * FileUploader.FileSelect * FileUploader.FileDrop * FileUploader.FileOver */ module .value('fileUploaderOptions', { url: '/', alias: 'file', headers: {}, queue: [], progress: 0, autoUpload: false, removeAfterUpload: false, method: 'POST', filters: [], formData: [], queueLimit: Number.MAX_VALUE, withCredentials: false }) .factory('FileUploader', ['fileUploaderOptions', '$rootScope', '$http', '$window', '$compile', function(fileUploaderOptions, $rootScope, $http, $window, $compile) { /** * Creates an instance of FileUploader * @param {Object} [options] * @constructor */ function FileUploader(options) { var settings = angular.copy(fileUploaderOptions); angular.extend(this, settings, options, { isUploading: false, _nextIndex: 0, _failFilterIndex: -1, _directives: {select: [], drop: [], over: []} }); // add default filters this.filters.unshift({name: 'queueLimit', fn: this._queueLimitFilter}); this.filters.unshift({name: 'folder', fn: this._folderFilter}); } /********************** * PUBLIC **********************/ /** * Checks a support the html5 uploader * @returns {Boolean} * @readonly */ FileUploader.prototype.isHTML5 = !!($window.File && $window.FormData); /** * Adds items to the queue * @param {File|HTMLInputElement|Object|FileList|Array<Object>} files * @param {Object} [options] * @param {Array<Function>|String} filters */ FileUploader.prototype.addToQueue = function(files, options, filters) { var list = this.isArrayLikeObject(files) ? files: [files]; var arrayOfFilters = this._getFilters(filters); var count = this.queue.length; var addedFileItems = []; angular.forEach(list, function(some /*{File|HTMLInputElement|Object}*/) { var temp = new FileUploader.FileLikeObject(some); if (this._isValidFile(temp, arrayOfFilters, options)) { var fileItem = new FileUploader.FileItem(this, some, options); addedFileItems.push(fileItem); this.queue.push(fileItem); this._onAfterAddingFile(fileItem); } else { var filter = this.filters[this._failFilterIndex]; this._onWhenAddingFileFailed(temp, filter, options); } }, this); if(this.queue.length !== count) { this._onAfterAddingAll(addedFileItems); this.progress = this._getTotalProgress(); } this._render(); if (this.autoUpload) this.uploadAll(); }; /** * Remove items from the queue. Remove last: index = -1 * @param {FileItem|Number} value */ FileUploader.prototype.removeFromQueue = function(value) { var index = this.getIndexOfItem(value); var item = this.queue[index]; if (item.isUploading) item.cancel(); this.queue.splice(index, 1); item._destroy(); this.progress = this._getTotalProgress(); }; /** * Clears the queue */ FileUploader.prototype.clearQueue = function() { while(this.queue.length) { this.queue[0].remove(); } this.progress = 0; }; /** * Uploads a item from the queue * @param {FileItem|Number} value */ FileUploader.prototype.uploadItem = function(value) { var index = this.getIndexOfItem(value); var item = this.queue[index]; var transport = this.isHTML5 ? '_xhrTransport' : '_iframeTransport'; item._prepareToUploading(); if(this.isUploading) return; this.isUploading = true; this[transport](item); }; /** * Cancels uploading of item from the queue * @param {FileItem|Number} value */ FileUploader.prototype.cancelItem = function(value) { var index = this.getIndexOfItem(value); var item = this.queue[index]; var prop = this.isHTML5 ? '_xhr' : '_form'; if (item && item.isUploading) item[prop].abort(); }; /** * Uploads all not uploaded items of queue */ FileUploader.prototype.uploadAll = function() { var items = this.getNotUploadedItems().filter(function(item) { return !item.isUploading; }); if (!items.length) return; angular.forEach(items, function(item) { item._prepareToUploading(); }); items[0].upload(); }; /** * Cancels all uploads */ FileUploader.prototype.cancelAll = function() { var items = this.getNotUploadedItems(); angular.forEach(items, function(item) { item.cancel(); }); }; /** * Returns "true" if value an instance of File * @param {*} value * @returns {Boolean} * @private */ FileUploader.prototype.isFile = function(value) { var fn = $window.File; return (fn && value instanceof fn); }; /** * Returns "true" if value an instance of FileLikeObject * @param {*} value * @returns {Boolean} * @private */ FileUploader.prototype.isFileLikeObject = function(value) { return value instanceof FileUploader.FileLikeObject; }; /** * Returns "true" if value is array like object * @param {*} value * @returns {Boolean} */ FileUploader.prototype.isArrayLikeObject = function(value) { return (angular.isObject(value) && 'length' in value); }; /** * Returns a index of item from the queue * @param {Item|Number} value * @returns {Number} */ FileUploader.prototype.getIndexOfItem = function(value) { return angular.isNumber(value) ? value : this.queue.indexOf(value); }; /** * Returns not uploaded items * @returns {Array} */ FileUploader.prototype.getNotUploadedItems = function() { return this.queue.filter(function(item) { return !item.isUploaded; }); }; /** * Returns items ready for upload * @returns {Array} */ FileUploader.prototype.getReadyItems = function() { return this.queue .filter(function(item) { return (item.isReady && !item.isUploading); }) .sort(function(item1, item2) { return item1.index - item2.index; }); }; /** * Destroys instance of FileUploader */ FileUploader.prototype.destroy = function() { angular.forEach(this._directives, function(key) { angular.forEach(this._directives[key], function(object) { object.destroy(); }, this); }, this); }; /** * Callback * @param {Array} fileItems */ FileUploader.prototype.onAfterAddingAll = function(fileItems) {}; /** * Callback * @param {FileItem} fileItem */ FileUploader.prototype.onAfterAddingFile = function(fileItem) {}; /** * Callback * @param {File|Object} item * @param {Object} filter * @param {Object} options * @private */ FileUploader.prototype.onWhenAddingFileFailed = function(item, filter, options) {}; /** * Callback * @param {FileItem} fileItem */ FileUploader.prototype.onBeforeUploadItem = function(fileItem) {}; /** * Callback * @param {FileItem} fileItem * @param {Number} progress */ FileUploader.prototype.onProgressItem = function(fileItem, progress) {}; /** * Callback * @param {Number} progress */ FileUploader.prototype.onProgressAll = function(progress) {}; /** * Callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers */ FileUploader.prototype.onSuccessItem = function(item, response, status, headers) {}; /** * Callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers */ FileUploader.prototype.onErrorItem = function(item, response, status, headers) {}; /** * Callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers */ FileUploader.prototype.onCancelItem = function(item, response, status, headers) {}; /** * Callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers */ FileUploader.prototype.onCompleteItem = function(item, response, status, headers) {}; /** * Callback */ FileUploader.prototype.onCompleteAll = function() {}; /********************** * PRIVATE **********************/ /** * Returns the total progress * @param {Number} [value] * @returns {Number} * @private */ FileUploader.prototype._getTotalProgress = function(value) { if(this.removeAfterUpload) return value || 0; var notUploaded = this.getNotUploadedItems().length; var uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length; var ratio = 100 / this.queue.length; var current = (value || 0) * ratio / 100; return Math.round(uploaded * ratio + current); }; /** * Returns array of filters * @param {Array<Function>|String} filters * @returns {Array<Function>} * @private */ FileUploader.prototype._getFilters = function(filters) { if (angular.isUndefined(filters)) return this.filters; if (angular.isArray(filters)) return filters; var names = filters.match(/[^\s,]+/g); return this.filters.filter(function(filter) { return names.indexOf(filter.name) !== -1; }, this); }; /** * Updates html * @private */ FileUploader.prototype._render = function() { if (!$rootScope.$$phase) $rootScope.$apply(); }; /** * Returns "true" if item is a file (not folder) * @param {File|FileLikeObject} item * @returns {Boolean} * @private */ FileUploader.prototype._folderFilter = function(item) { return !!(item.size || item.type); }; /** * Returns "true" if the limit has not been reached * @returns {Boolean} * @private */ FileUploader.prototype._queueLimitFilter = function() { return this.queue.length < this.queueLimit; }; /** * Returns "true" if file pass all filters * @param {File|Object} file * @param {Array<Function>} filters * @param {Object} options * @returns {Boolean} * @private */ FileUploader.prototype._isValidFile = function(file, filters, options) { this._failFilterIndex = -1; return !filters.length ? true : filters.every(function(filter) { this._failFilterIndex++; return filter.fn.call(this, file, options); }, this); }; /** * Checks whether upload successful * @param {Number} status * @returns {Boolean} * @private */ FileUploader.prototype._isSuccessCode = function(status) { return (status >= 200 && status < 300) || status === 304; }; /** * Transforms the server response * @param {*} response * @param {Object} headers * @returns {*} * @private */ FileUploader.prototype._transformResponse = function(response, headers) { var headersGetter = this._headersGetter(headers); angular.forEach($http.defaults.transformResponse, function(transformFn) { response = transformFn(response, headersGetter); }); return response; }; /** * Parsed response headers * @param headers * @returns {Object} * @see https://github.com/angular/angular.js/blob/master/src/ng/http.js * @private */ FileUploader.prototype._parseHeaders = function(headers) { var parsed = {}, key, val, i; if (!headers) return parsed; angular.forEach(headers.split('\n'), function(line) { i = line.indexOf(':'); key = line.slice(0, i).trim().toLowerCase(); val = line.slice(i + 1).trim(); if (key) { parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; } }); return parsed; }; /** * Returns function that returns headers * @param {Object} parsedHeaders * @returns {Function} * @private */ FileUploader.prototype._headersGetter = function(parsedHeaders) { return function(name) { if (name) { return parsedHeaders[name.toLowerCase()] || null; } return parsedHeaders; }; }; /** * The XMLHttpRequest transport * @param {FileItem} item * @private */ FileUploader.prototype._xhrTransport = function(item) { var xhr = item._xhr = new XMLHttpRequest(); var form = new FormData(); var that = this; that._onBeforeUploadItem(item); angular.forEach(item.formData, function(obj) { angular.forEach(obj, function(value, key) { form.append(key, value); }); }); form.append(item.alias, item._file, item.file.name); xhr.upload.onprogress = function(event) { var progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); that._onProgressItem(item, progress); }; xhr.onload = function() { var headers = that._parseHeaders(xhr.getAllResponseHeaders()); var response = that._transformResponse(xhr.response, headers); var gist = that._isSuccessCode(xhr.status) ? 'Success' : 'Error'; var method = '_on' + gist + 'Item'; that[method](item, response, xhr.status, headers); that._onCompleteItem(item, response, xhr.status, headers); }; xhr.onerror = function() { var headers = that._parseHeaders(xhr.getAllResponseHeaders()); var response = that._transformResponse(xhr.response, headers); that._onErrorItem(item, response, xhr.status, headers); that._onCompleteItem(item, response, xhr.status, headers); }; xhr.onabort = function() { var headers = that._parseHeaders(xhr.getAllResponseHeaders()); var response = that._transformResponse(xhr.response, headers); that._onCancelItem(item, response, xhr.status, headers); that._onCompleteItem(item, response, xhr.status, headers); }; xhr.open(item.method, item.url, true); xhr.withCredentials = item.withCredentials; angular.forEach(item.headers, function(value, name) { xhr.setRequestHeader(name, value); }); xhr.send(form); this._render(); }; /** * The IFrame transport * @param {FileItem} item * @private */ FileUploader.prototype._iframeTransport = function(item) { var form = angular.element('<form style="display: none;" />'); var iframe = angular.element('<iframe name="iframeTransport' + Date.now() + '">'); var input = item._input; var that = this; if (item._form) item._form.replaceWith(input); // remove old form item._form = form; // save link to new form that._onBeforeUploadItem(item); input.prop('name', item.alias); angular.forEach(item.formData, function(obj) { angular.forEach(obj, function(value, key) { var element = angular.element('<input type="hidden" name="' + key + '" />'); element.val(value); form.append(element); }); }); form.prop({ action: item.url, method: 'POST', target: iframe.prop('name'), enctype: 'multipart/form-data', encoding: 'multipart/form-data' // old IE }); iframe.bind('load', function() { try { // Fix for legacy IE browsers that loads internal error page // when failed WS response received. In consequence iframe // content access denied error is thrown becouse trying to // access cross domain page. When such thing occurs notifying // with empty response object. See more info at: // http://stackoverflow.com/questions/151362/access-is-denied-error-on-accessing-iframe-document-object // Note that if non standard 4xx or 5xx error code returned // from WS then response content can be accessed without error // but 'XHR' status becomes 200. In order to avoid confusion // returning response via same 'success' event handler. // fixed angular.contents() for iframes var html = iframe[0].contentDocument.body.innerHTML; } catch (e) {} var xhr = {response: html, status: 200, dummy: true}; var headers = {}; var response = that._transformResponse(xhr.response, headers); that._onSuccessItem(item, response, xhr.status, headers); that._onCompleteItem(item, response, xhr.status, headers); }); form.abort = function() { var xhr = {status: 0, dummy: true}; var headers = {}; var response; iframe.unbind('load').prop('src', 'javascript:false;'); form.replaceWith(input); that._onCancelItem(item, response, xhr.status, headers); that._onCompleteItem(item, response, xhr.status, headers); }; input.after(form); form.append(input).append(iframe); form[0].submit(); this._render(); }; /** * Inner callback * @param {File|Object} item * @param {Object} filter * @param {Object} options * @private */ FileUploader.prototype._onWhenAddingFileFailed = function(item, filter, options) { this.onWhenAddingFileFailed(item, filter, options); }; /** * Inner callback * @param {FileItem} item */ FileUploader.prototype._onAfterAddingFile = function(item) { this.onAfterAddingFile(item); }; /** * Inner callback * @param {Array<FileItem>} items */ FileUploader.prototype._onAfterAddingAll = function(items) { this.onAfterAddingAll(items); }; /** * Inner callback * @param {FileItem} item * @private */ FileUploader.prototype._onBeforeUploadItem = function(item) { item._onBeforeUpload(); this.onBeforeUploadItem(item); }; /** * Inner callback * @param {FileItem} item * @param {Number} progress * @private */ FileUploader.prototype._onProgressItem = function(item, progress) { var total = this._getTotalProgress(progress); this.progress = total; item._onProgress(progress); this.onProgressItem(item, progress); this.onProgressAll(total); this._render(); }; /** * Inner callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileUploader.prototype._onSuccessItem = function(item, response, status, headers) { item._onSuccess(response, status, headers); this.onSuccessItem(item, response, status, headers); }; /** * Inner callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileUploader.prototype._onErrorItem = function(item, response, status, headers) { item._onError(response, status, headers); this.onErrorItem(item, response, status, headers); }; /** * Inner callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileUploader.prototype._onCancelItem = function(item, response, status, headers) { item._onCancel(response, status, headers); this.onCancelItem(item, response, status, headers); }; /** * Inner callback * @param {FileItem} item * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileUploader.prototype._onCompleteItem = function(item, response, status, headers) { item._onComplete(response, status, headers); this.onCompleteItem(item, response, status, headers); var nextItem = this.getReadyItems()[0]; this.isUploading = false; if(angular.isDefined(nextItem)) { nextItem.upload(); return; } this.onCompleteAll(); this.progress = this._getTotalProgress(); this._render(); }; /********************** * STATIC **********************/ /** * @borrows FileUploader.prototype.isFile */ FileUploader.isFile = FileUploader.prototype.isFile; /** * @borrows FileUploader.prototype.isFileLikeObject */ FileUploader.isFileLikeObject = FileUploader.prototype.isFileLikeObject; /** * @borrows FileUploader.prototype.isArrayLikeObject */ FileUploader.isArrayLikeObject = FileUploader.prototype.isArrayLikeObject; /** * @borrows FileUploader.prototype.isHTML5 */ FileUploader.isHTML5 = FileUploader.prototype.isHTML5; /** * Inherits a target (Class_1) by a source (Class_2) * @param {Function} target * @param {Function} source */ FileUploader.inherit = function(target, source) { target.prototype = Object.create(source.prototype); target.prototype.constructor = target; target.super_ = source; }; FileUploader.FileLikeObject = FileLikeObject; FileUploader.FileItem = FileItem; FileUploader.FileDirective = FileDirective; FileUploader.FileSelect = FileSelect; FileUploader.FileDrop = FileDrop; FileUploader.FileOver = FileOver; // --------------------------- /** * Creates an instance of FileLikeObject * @param {File|HTMLInputElement|Object} fileOrInput * @constructor */ function FileLikeObject(fileOrInput) { var isInput = angular.isElement(fileOrInput); var fakePathOrObject = isInput ? fileOrInput.value : fileOrInput; var postfix = angular.isString(fakePathOrObject) ? 'FakePath' : 'Object'; var method = '_createFrom' + postfix; this[method](fakePathOrObject); } /** * Creates file like object from fake path string * @param {String} path * @private */ FileLikeObject.prototype._createFromFakePath = function(path) { this.lastModifiedDate = null; this.size = null; this.type = 'like/' + path.slice(path.lastIndexOf('.') + 1).toLowerCase(); this.name = path.slice(path.lastIndexOf('/') + path.lastIndexOf('\\') + 2); }; /** * Creates file like object from object * @param {File|FileLikeObject} object * @private */ FileLikeObject.prototype._createFromObject = function(object) { this.lastModifiedDate = angular.copy(object.lastModifiedDate); this.size = object.size; this.type = object.type; this.name = object.name; }; // --------------------------- /** * Creates an instance of FileItem * @param {FileUploader} uploader * @param {File|HTMLInputElement|Object} some * @param {Object} options * @constructor */ function FileItem(uploader, some, options) { var isInput = angular.isElement(some); var input = isInput ? angular.element(some) : null; var file = !isInput ? some : null; angular.extend(this, { url: uploader.url, alias: uploader.alias, headers: angular.copy(uploader.headers), formData: angular.copy(uploader.formData), removeAfterUpload: uploader.removeAfterUpload, withCredentials: uploader.withCredentials, method: uploader.method }, options, { uploader: uploader, file: new FileUploader.FileLikeObject(some), isReady: false, isUploading: false, isUploaded: false, isSuccess: false, isCancel: false, isError: false, progress: 0, index: null, _file: file, _input: input }); if (input) this._replaceNode(input); } /********************** * PUBLIC **********************/ /** * Uploads a FileItem */ FileItem.prototype.upload = function() { this.uploader.uploadItem(this); }; /** * Cancels uploading of FileItem */ FileItem.prototype.cancel = function() { this.uploader.cancelItem(this); }; /** * Removes a FileItem */ FileItem.prototype.remove = function() { this.uploader.removeFromQueue(this); }; /** * Callback * @private */ FileItem.prototype.onBeforeUpload = function() {}; /** * Callback * @param {Number} progress * @private */ FileItem.prototype.onProgress = function(progress) {}; /** * Callback * @param {*} response * @param {Number} status * @param {Object} headers */ FileItem.prototype.onSuccess = function(response, status, headers) {}; /** * Callback * @param {*} response * @param {Number} status * @param {Object} headers */ FileItem.prototype.onError = function(response, status, headers) {}; /** * Callback * @param {*} response * @param {Number} status * @param {Object} headers */ FileItem.prototype.onCancel = function(response, status, headers) {}; /** * Callback * @param {*} response * @param {Number} status * @param {Object} headers */ FileItem.prototype.onComplete = function(response, status, headers) {}; /********************** * PRIVATE **********************/ /** * Inner callback */ FileItem.prototype._onBeforeUpload = function() { this.isReady = true; this.isUploading = true; this.isUploaded = false; this.isSuccess = false; this.isCancel = false; this.isError = false; this.progress = 0; this.onBeforeUpload(); }; /** * Inner callback * @param {Number} progress * @private */ FileItem.prototype._onProgress = function(progress) { this.progress = progress; this.onProgress(progress); }; /** * Inner callback * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileItem.prototype._onSuccess = function(response, status, headers) { this.isReady = false; this.isUploading = false; this.isUploaded = true; this.isSuccess = true; this.isCancel = false; this.isError = false; this.progress = 100; this.index = null; this.onSuccess(response, status, headers); }; /** * Inner callback * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileItem.prototype._onError = function(response, status, headers) { this.isReady = false; this.isUploading = false; this.isUploaded = true; this.isSuccess = false; this.isCancel = false; this.isError = true; this.progress = 0; this.index = null; this.onError(response, status, headers); }; /** * Inner callback * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileItem.prototype._onCancel = function(response, status, headers) { this.isReady = false; this.isUploading = false; this.isUploaded = false; this.isSuccess = false; this.isCancel = true; this.isError = false; this.progress = 0; this.index = null; this.onCancel(response, status, headers); }; /** * Inner callback * @param {*} response * @param {Number} status * @param {Object} headers * @private */ FileItem.prototype._onComplete = function(response, status, headers) { this.onComplete(response, status, headers); if (this.removeAfterUpload) this.remove(); }; /** * Destroys a FileItem */ FileItem.prototype._destroy = function() { if (this._input) this._input.remove(); if (this._form) this._form.remove(); delete this._form; delete this._input; }; /** * Prepares to uploading * @private */ FileItem.prototype._prepareToUploading = function() { this.index = this.index || ++this.uploader._nextIndex; this.isReady = true; }; /** * Replaces input element on his clone * @param {JQLite|jQuery} input * @private */ FileItem.prototype._replaceNode = function(input) { var clone = $compile(input.clone())(input.scope()); clone.prop('value', null); // FF fix input.css('display', 'none'); input.after(clone); // remove jquery dependency }; // --------------------------- /** * Creates instance of {FileDirective} object * @param {Object} options * @param {Object} options.uploader * @param {HTMLElement} options.element * @param {Object} options.events * @param {String} options.prop * @constructor */ function FileDirective(options) { angular.extend(this, options); this.uploader._directives[this.prop].push(this); this._saveLinks(); this.bind(); } /** * Map of events * @type {Object} */ FileDirective.prototype.events = {}; /** * Binds events handles */ FileDirective.prototype.bind = function() { for(var key in this.events) { var prop = this.events[key]; this.element.bind(key, this[prop]); } }; /** * Unbinds events handles */ FileDirective.prototype.unbind = function() { for(var key in this.events) { this.element.unbind(key, this.events[key]); } }; /** * Destroys directive */ FileDirective.prototype.destroy = function() { var index = this.uploader._directives[this.prop].indexOf(this); this.uploader._directives[this.prop].splice(index, 1); this.unbind(); // this.element = null; }; /** * Saves links to functions * @private */ FileDirective.prototype._saveLinks = function() { for(var key in this.events) { var prop = this.events[key]; this[prop] = this[prop].bind(this); } }; // --------------------------- FileUploader.inherit(FileSelect, FileDirective); /** * Creates instance of {FileSelect} object * @param {Object} options * @constructor */ function FileSelect(options) { FileSelect.super_.apply(this, arguments); if(!this.uploader.isHTML5) { this.element.removeAttr('multiple'); } this.element.prop('value', null); // FF fix } /** * Map of events * @type {Object} */ FileSelect.prototype.events = { $destroy: 'destroy', change: 'onChange' }; /** * Name of property inside uploader._directive object * @type {String} */ FileSelect.prototype.prop = 'select'; /** * Returns options * @return {Object|undefined} */ FileSelect.prototype.getOptions = function() {}; /** * Returns filters * @return {Array<Function>|String|undefined} */ FileSelect.prototype.getFilters = function() {}; /** * If returns "true" then HTMLInputElement will be cleared * @returns {Boolean} */ FileSelect.prototype.isEmptyAfterSelection = function() { return !!this.element.attr('multiple'); }; /** * Event handler */ FileSelect.prototype.onChange = function() { var files = this.uploader.isHTML5 ? this.element[0].files : this.element[0]; var options = this.getOptions(); var filters = this.getFilters(); if (!this.uploader.isHTML5) this.destroy(); this.uploader.addToQueue(files, options, filters); if (this.isEmptyAfterSelection()) this.element.prop('value', null); }; // --------------------------- FileUploader.inherit(FileDrop, FileDirective); /** * Creates instance of {FileDrop} object * @param {Object} options * @constructor */ function FileDrop(options) { FileDrop.super_.apply(this, arguments); } /** * Map of events * @type {Object} */ FileDrop.prototype.events = { $destroy: 'destroy', drop: 'onDrop', dragover: 'onDragOver', dragleave: 'onDragLeave' }; /** * Name of property inside uploader._directive object * @type {String} */ FileDrop.prototype.prop = 'drop'; /** * Returns options * @return {Object|undefined} */ FileDrop.prototype.getOptions = function() {}; /** * Returns filters * @return {Array<Function>|String|undefined} */ FileDrop.prototype.getFilters = function() {}; /** * Event handler */ FileDrop.prototype.onDrop = function(event) { var transfer = this._getTransfer(event); if (!transfer) return; var options = this.getOptions(); var filters = this.getFilters(); this._preventAndStop(event); angular.forEach(this.uploader._directives.over, this._removeOverClass, this); this.uploader.addToQueue(transfer.files, options, filters); }; /** * Event handler */ FileDrop.prototype.onDragOver = function(event) { var transfer = this._getTransfer(event); if(!this._haveFiles(transfer.types)) return; transfer.dropEffect = 'copy'; this._preventAndStop(event); angular.forEach(this.uploader._directives.over, this._addOverClass, this); }; /** * Event handler */ FileDrop.prototype.onDragLeave = function(event) { if (event.currentTarget !== this.element[0]) return; this._preventAndStop(event); angular.forEach(this.uploader._directives.over, this._removeOverClass, this); }; /** * Helper */ FileDrop.prototype._getTransfer = function(event) { return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix; }; /** * Helper */ FileDrop.prototype._preventAndStop = function(event) { event.preventDefault(); event.stopPropagation(); }; /** * Returns "true" if types contains files * @param {Object} types */ FileDrop.prototype._haveFiles = function(types) { if (!types) return false; if (types.indexOf) { return types.indexOf('Files') !== -1; } else if(types.contains) { return types.contains('Files'); } else { return false; } }; /** * Callback */ FileDrop.prototype._addOverClass = function(item) { item.addOverClass(); }; /** * Callback */ FileDrop.prototype._removeOverClass = function(item) { item.removeOverClass(); }; // --------------------------- FileUploader.inherit(FileOver, FileDirective); /** * Creates instance of {FileDrop} object * @param {Object} options * @constructor */ function FileOver(options) { FileOver.super_.apply(this, arguments); } /** * Map of events * @type {Object} */ FileOver.prototype.events = { $destroy: 'destroy' }; /** * Name of property inside uploader._directive object * @type {String} */ FileOver.prototype.prop = 'over'; /** * Over class * @type {string} */ FileOver.prototype.overClass = 'nv-file-over'; /** * Adds over class */ FileOver.prototype.addOverClass = function() { this.element.addClass(this.getOverClass()); }; /** * Removes over class */ FileOver.prototype.removeOverClass = function() { this.element.removeClass(this.getOverClass()); }; /** * Returns over class * @returns {String} */ FileOver.prototype.getOverClass = function() { return this.overClass; }; return FileUploader; }]) .directive('nvFileSelect', ['$parse', 'FileUploader', function($parse, FileUploader) { return { link: function(scope, element, attributes) { var uploader = scope.$eval(attributes.uploader); if (!(uploader instanceof FileUploader)) { throw new TypeError('"Uploader" must be an instance of FileUploader'); } var object = new FileUploader.FileSelect({ uploader: uploader, element: element }); object.getOptions = $parse(attributes.options).bind(object, scope); object.getFilters = function() {return attributes.filters;}; } }; }]) .directive('nvFileDrop', ['$parse', 'FileUploader', function($parse, FileUploader) { return { link: function(scope, element, attributes) { var uploader = scope.$eval(attributes.uploader); if (!(uploader instanceof FileUploader)) { throw new TypeError('"Uploader" must be an instance of FileUploader'); } if (!uploader.isHTML5) return; var object = new FileUploader.FileDrop({ uploader: uploader, element: element }); object.getOptions = $parse(attributes.options).bind(object, scope); object.getFilters = function() {return attributes.filters;}; } }; }]) .directive('nvFileOver', ['FileUploader', function(FileUploader) { return { link: function(scope, element, attributes) { var uploader = scope.$eval(attributes.uploader); if (!(uploader instanceof FileUploader)) { throw new TypeError('"Uploader" must be an instance of FileUploader'); } var object = new FileUploader.FileOver({ uploader: uploader, element: element }); object.getOverClass = function() { return attributes.overClass || this.overClass; }; } }; }]) return module; }));