text: ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', function ($parse, $timeout, $compile, Upload) {
    var generatedElems = [];
  
    function isDelayedClickSupported(ua) {
      // fix for android native browser < 4.4 and safari windows
      var m = ua.match(/Android[^\d]*(\d+)\.(\d+)/);
      if (m && m.length > 2) {
        var v = Upload.defaults.androidFixMinorVersion || 4;
        return parseInt(m[1]) < 4 || (parseInt(m[1]) === v && parseInt(m[2]) < v);
      }
  
      // safari on windows
      return ua.indexOf('Chrome') === -1 && /.*Windows.*Safari.*/.test(ua);
    }
  
    function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, upload) {
      /** @namespace attr.ngfSelect */
      /** @namespace attr.ngfChange */
      /** @namespace attr.ngModel */
      /** @namespace attr.ngfModelOptions */
      /** @namespace attr.ngfMultiple */
      /** @namespace attr.ngfCapture */
      /** @namespace attr.ngfValidate */
      /** @namespace attr.ngfKeep */
      var attrGetter = function (name, scope) {
        return upload.attrGetter(name, attr, scope);
      };
  
      function isInputTypeFile() {
        return elem[0].tagName.toLowerCase() === 'input' && attr.type && attr.type.toLowerCase() === 'file';
      }
  
      function fileChangeAttr() {
        return attrGetter('ngfChange') || attrGetter('ngfSelect');
      }
  
      function changeFn(evt) {
        if (upload.shouldUpdateOn('change', attr, scope)) {
          var fileList = evt.__files_ || (evt.target && evt.target.files), files = [];
          /* Handle duplicate call in  IE11 */
          if (!fileList) return;
          for (var i = 0; i < fileList.length; i++) {
            files.push(fileList[i]);
          }
          upload.updateModel(ngModel, attr, scope, fileChangeAttr(),
            files.length ? files : null, evt);
        }
      }
  
      upload.registerModelChangeValidator(ngModel, attr, scope);
  
      var unwatches = [];
      if (attrGetter('ngfMultiple')) {
        unwatches.push(scope.$watch(attrGetter('ngfMultiple'), function () {
          fileElem.attr('multiple', attrGetter('ngfMultiple', scope));
        }));
      }
      if (attrGetter('ngfCapture')) {
        unwatches.push(scope.$watch(attrGetter('ngfCapture'), function () {
          fileElem.attr('capture', attrGetter('ngfCapture', scope));
        }));
      }
      if (attrGetter('ngfAccept')) {
        unwatches.push(scope.$watch(attrGetter('ngfAccept'), function () {
          fileElem.attr('accept', attrGetter('ngfAccept', scope));
        }));
      }
      unwatches.push(attr.$observe('accept', function () {
        fileElem.attr('accept', attrGetter('accept'));
      }));
      function bindAttrToFileInput(fileElem, label) {
        function updateId(val) {
          fileElem.attr('id', 'ngf-' + val);
          label.attr('id', 'ngf-label-' + val);
        }
  
        for (var i = 0; i < elem[0].attributes.length; i++) {
          var attribute = elem[0].attributes[i];
          if (attribute.name !== 'type' && attribute.name !== 'class' && attribute.name !== 'style') {
            if (attribute.name === 'id') {
              updateId(attribute.value);
              unwatches.push(attr.$observe('id', updateId));
            } else {
              fileElem.attr(attribute.name, (!attribute.value && (attribute.name === 'required' ||
              attribute.name === 'multiple')) ? attribute.name : attribute.value);
            }
          }
        }
      }
  
      function createFileInput() {
        if (isInputTypeFile()) {
          return elem;
        }
  
        var fileElem = angular.element('<input type="file">');
  
        var label = angular.element('<label>upload</label>');
        label.css('visibility', 'hidden').css('position', 'absolute').css('overflow', 'hidden')
          .css('width', '0px').css('height', '0px').css('border', 'none')
          .css('margin', '0px').css('padding', '0px').attr('tabindex', '-1');
        bindAttrToFileInput(fileElem, label);
  
        generatedElems.push({el: elem, ref: label});
  
        document.body.appendChild(label.append(fileElem)[0]);
  
        return fileElem;
      }
  
      function clickHandler(evt) {
        if (elem.attr('disabled')) return false;
        if (attrGetter('ngfSelectDisabled', scope)) return;
  
        var r = detectSwipe(evt);
        // prevent the click if it is a swipe
        if (r != null) return r;
  
        resetModel(evt);
  
        // fix for md when the element is removed from the DOM and added back #460
        try {
          if (!isInputTypeFile() && !document.body.contains(fileElem[0])) {
            generatedElems.push({el: elem, ref: fileElem.parent()});
            document.body.appendChild(fileElem.parent()[0]);
            fileElem.bind('change', changeFn);
          }
        } catch (e) {/*ignore*/
        }
  
        if (isDelayedClickSupported(navigator.userAgent)) {
          setTimeout(function () {
            fileElem[0].click();
          }, 0);
        } else {
          fileElem[0].click();
        }
  
        return false;
      }
  
  
      var initialTouchStartY = 0;
      var initialTouchStartX = 0;
  
      function detectSwipe(evt) {
        var touches = evt.changedTouches || (evt.originalEvent && evt.originalEvent.changedTouches);
        if (touches) {
          if (evt.type === 'touchstart') {
            initialTouchStartX = touches[0].clientX;
            initialTouchStartY = touches[0].clientY;
            return true; // don't block event default
          } else {
            // prevent scroll from triggering event
            if (evt.type === 'touchend') {
              var currentX = touches[0].clientX;
              var currentY = touches[0].clientY;
              if ((Math.abs(currentX - initialTouchStartX) > 20) ||
                (Math.abs(currentY - initialTouchStartY) > 20)) {
                evt.stopPropagation();
                evt.preventDefault();
                return false;
              }
            }
            return true;
          }
        }
      }
  
      var fileElem = elem;
  
      function resetModel(evt) {
        if (upload.shouldUpdateOn('click', attr, scope) && fileElem.val()) {
          fileElem.val(null);
          upload.updateModel(ngModel, attr, scope, fileChangeAttr(), null, evt, true);
        }
      }
  
      if (!isInputTypeFile()) {
        fileElem = createFileInput();
      }
      fileElem.bind('change', changeFn);
  
      if (!isInputTypeFile()) {
        elem.bind('click touchstart touchend', clickHandler);
      } else {
        elem.bind('click', resetModel);
      }
  
      function ie10SameFileSelectFix(evt) {
        if (fileElem && !fileElem.attr('__ngf_ie10_Fix_')) {
          if (!fileElem[0].parentNode) {
            fileElem = null;
            return;
          }
          evt.preventDefault();
          evt.stopPropagation();
          fileElem.unbind('click');
          var clone = fileElem.clone();
          fileElem.replaceWith(clone);
          fileElem = clone;
          fileElem.attr('__ngf_ie10_Fix_', 'true');
          fileElem.bind('change', changeFn);
          fileElem.bind('click', ie10SameFileSelectFix);
          fileElem[0].click();
          return false;
        } else {
          fileElem.removeAttr('__ngf_ie10_Fix_');
        }
      }
  
      if (navigator.appVersion.indexOf('MSIE 10') !== -1) {
        fileElem.bind('click', ie10SameFileSelectFix);
      }
  
      if (ngModel) ngModel.$formatters.push(function (val) {
        if (val == null || val.length === 0) {
          if (fileElem.val()) {
            fileElem.val(null);
          }
        }
        return val;
      });
  
      scope.$on('$destroy', function () {
        if (!isInputTypeFile()) fileElem.parent().remove();
        angular.forEach(unwatches, function (unwatch) {
          unwatch();
        });
      });
  
      $timeout(function () {
        for (var i = 0; i < generatedElems.length; i++) {
          var g = generatedElems[i];
          if (!document.body.contains(g.el[0])) {
            generatedElems.splice(i, 1);
            g.ref.remove();
          }
        }
      });
  
      if (window.FileAPI && window.FileAPI.ngfFixIE) {
        window.FileAPI.ngfFixIE(elem, fileElem, changeFn);
      }
    }
  
    return {
      restrict: 'AEC',
      require: '?ngModel',
      link: function (scope, elem, attr, ngModel) {
        linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, Upload);
      }
    };
  }]);
  