UNPKG

ng-file-upload-haikulearning

Version:

An AngularJS directive for file upload using HTML5 with FileAPI polyfill for unsupported browsers

234 lines (199 loc) 7.55 kB
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.ngModelOptions */ /** @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 = []; 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 = []; unwatches.push(scope.$watch(attrGetter('ngfMultiple'), function () { fileElem.attr('multiple', attrGetter('ngfMultiple', scope)); })); unwatches.push(scope.$watch(attrGetter('ngfCapture'), function () { fileElem.attr('capture', attrGetter('ngfCapture', scope)); })); unwatches.push(scope.$watch(attrGetter('ngfAccept'), function () { fileElem.attr('accept', attrGetter('ngfAccept', scope)); })); attr.$observe('accept', function () { fileElem.attr('accept', attrGetter('accept')); }); unwatches.push(function () { if (attr.$$observers) delete attr.$$observers.accept; }); function bindAttrToFileInput(fileElem) { if (elem !== fileElem) { 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.value == null || attribute.value === '') { if (attribute.name === 'required') attribute.value = 'required'; if (attribute.name === 'multiple') attribute.value = 'multiple'; } fileElem.attr(attribute.name, attribute.name === 'id' ? 'ngf-' + attribute.value : attribute.value); } } } } function createFileInput() { if (isInputTypeFile()) { return elem; } var fileElem = angular.element('<input type="file">'); bindAttrToFileInput(fileElem); 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'); generatedElems.push({el: elem, ref: label}); document.body.appendChild(label.append(fileElem)[0]); return fileElem; } var initialTouchStartY = 0; function clickHandler(evt) { if (elem.attr('disabled')) return false; if (attrGetter('ngfSelectDisabled', scope)) return; var r = handleTouch(evt); 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; } function handleTouch(evt) { var touches = evt.changedTouches || (evt.originalEvent && evt.originalEvent.changedTouches); if (evt.type === 'touchstart') { initialTouchStartY = touches ? touches[0].clientY : 0; return true; // don't block event default } else { evt.stopPropagation(); evt.preventDefault(); // prevent scroll from triggering event if (evt.type === 'touchend') { var currentLocation = touches ? touches[0].clientY : 0; if (Math.abs(currentLocation - initialTouchStartY) > 20) return false; } } } 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); } }; }]);