text: /*
   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;
  }));