电子档案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

982 lines
33 KiB

  1. /**!
  2. * AngularJS file upload/drop directive and service with progress and abort
  3. * @author Danial <danial.farid@gmail.com>
  4. * @version 4.1.0
  5. */
  6. (function () {
  7. var key, i;
  8. function patchXHR(fnName, newFn) {
  9. window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
  10. }
  11. if (window.XMLHttpRequest && !window.XMLHttpRequest.__isFileAPIShim) {
  12. patchXHR('setRequestHeader', function (orig) {
  13. return function (header, value) {
  14. if (header === '__setXHR_') {
  15. var val = value(this);
  16. // fix for angular < 1.2.0
  17. if (val instanceof Function) {
  18. val(this);
  19. }
  20. } else {
  21. orig.apply(this, arguments);
  22. }
  23. }
  24. });
  25. }
  26. var ngFileUpload = angular.module('ngFileUpload', []);
  27. ngFileUpload.version = '4.1.0';
  28. ngFileUpload.service('Upload', ['$http', '$q', '$timeout', function ($http, $q, $timeout) {
  29. function sendHttp(config) {
  30. config.method = config.method || 'POST';
  31. config.headers = config.headers || {};
  32. config.transformRequest = config.transformRequest || function (data, headersGetter) {
  33. if (window.ArrayBuffer && data instanceof window.ArrayBuffer) {
  34. return data;
  35. }
  36. return $http.defaults.transformRequest[0](data, headersGetter);
  37. };
  38. var deferred = $q.defer();
  39. var promise = deferred.promise;
  40. config.headers['__setXHR_'] = function () {
  41. return function (xhr) {
  42. if (!xhr) return;
  43. config.__XHR = xhr;
  44. config.xhrFn && config.xhrFn(xhr);
  45. xhr.upload.addEventListener('progress', function (e) {
  46. e.config = config;
  47. deferred.notify ? deferred.notify(e) : promise.progress_fn && $timeout(function () {
  48. promise.progress_fn(e)
  49. });
  50. }, false);
  51. //fix for firefox not firing upload progress end, also IE8-9
  52. xhr.upload.addEventListener('load', function (e) {
  53. if (e.lengthComputable) {
  54. e.config = config;
  55. deferred.notify ? deferred.notify(e) : promise.progress_fn && $timeout(function () {
  56. promise.progress_fn(e)
  57. });
  58. }
  59. }, false);
  60. };
  61. };
  62. $http(config).then(function (r) {
  63. deferred.resolve(r)
  64. }, function (e) {
  65. deferred.reject(e)
  66. }, function (n) {
  67. deferred.notify(n)
  68. });
  69. promise.success = function (fn) {
  70. promise.then(function (response) {
  71. fn(response.data, response.status, response.headers, config);
  72. });
  73. return promise;
  74. };
  75. promise.error = function (fn) {
  76. promise.then(null, function (response) {
  77. fn(response.data, response.status, response.headers, config);
  78. });
  79. return promise;
  80. };
  81. promise.progress = function (fn) {
  82. promise.progress_fn = fn;
  83. promise.then(null, null, function (update) {
  84. fn(update);
  85. });
  86. return promise;
  87. };
  88. promise.abort = function () {
  89. if (config.__XHR) {
  90. $timeout(function () {
  91. config.__XHR.abort();
  92. });
  93. }
  94. return promise;
  95. };
  96. promise.xhr = function (fn) {
  97. config.xhrFn = (function (origXhrFn) {
  98. return function () {
  99. origXhrFn && origXhrFn.apply(promise, arguments);
  100. fn.apply(promise, arguments);
  101. }
  102. })(config.xhrFn);
  103. return promise;
  104. };
  105. return promise;
  106. }
  107. this.upload = function (config) {
  108. config.headers = config.headers || {};
  109. config.headers['Content-Type'] = undefined;
  110. config.transformRequest = config.transformRequest ?
  111. (angular.isArray(config.transformRequest) ?
  112. config.transformRequest : [config.transformRequest]) : [];
  113. config.transformRequest.push(function (data) {
  114. var formData = new FormData();
  115. var allFields = {};
  116. for (key in config.fields) {
  117. if (config.fields.hasOwnProperty(key)) {
  118. allFields[key] = config.fields[key];
  119. }
  120. }
  121. if (data) allFields['data'] = data;
  122. if (config.formDataAppender) {
  123. for (key in allFields) {
  124. if (allFields.hasOwnProperty(key)) {
  125. config.formDataAppender(formData, key, allFields[key]);
  126. }
  127. }
  128. } else {
  129. for (key in allFields) {
  130. if (allFields.hasOwnProperty(key)) {
  131. var val = allFields[key];
  132. if (val !== undefined) {
  133. if (angular.isDate(val)) {
  134. val = val.toISOString();
  135. }
  136. if (angular.isString(val)) {
  137. formData.append(key, val);
  138. } else {
  139. if (config.sendObjectsAsJsonBlob && angular.isObject(val)) {
  140. formData.append(key, new Blob([val], {type: 'application/json'}));
  141. } else {
  142. formData.append(key, JSON.stringify(val));
  143. }
  144. }
  145. }
  146. }
  147. }
  148. }
  149. if (config.file != null) {
  150. var fileFormName = config.fileFormDataName || 'file';
  151. if (angular.isArray(config.file)) {
  152. var isFileFormNameString = angular.isString(fileFormName);
  153. for (var i = 0; i < config.file.length; i++) {
  154. formData.append(isFileFormNameString ? fileFormName : fileFormName[i], config.file[i],
  155. (config.fileName && config.fileName[i]) || config.file[i].name);
  156. }
  157. } else {
  158. formData.append(fileFormName, config.file, config.fileName || config.file.name);
  159. }
  160. }
  161. return formData;
  162. });
  163. return sendHttp(config);
  164. };
  165. this.http = function (config) {
  166. return sendHttp(config);
  167. };
  168. }]);
  169. ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile',
  170. function ($parse, $timeout, $compile) {
  171. return {
  172. restrict: 'AEC',
  173. require: '?ngModel',
  174. link: function (scope, elem, attr, ngModel) {
  175. linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile);
  176. }
  177. }
  178. }]);
  179. function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile) {
  180. function isInputTypeFile() {
  181. return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file';
  182. }
  183. var changeFnAttr = attr.ngfChange || (attr.ngfSelect && attr.ngfSelect.indexOf('(') > 0);
  184. var isUpdating = false;
  185. function changeFn(evt) {
  186. if (!isUpdating) {
  187. isUpdating = true;
  188. try {
  189. var fileList = evt.__files_ || (evt.target && evt.target.files);
  190. var files = [], rejFiles = [];
  191. for (var i = 0; i < fileList.length; i++) {
  192. var file = fileList.item(i);
  193. if (validate(scope, $parse, attr, file, evt)) {
  194. files.push(file);
  195. } else {
  196. rejFiles.push(file);
  197. }
  198. }
  199. updateModel($parse, $timeout, scope, ngModel, attr, changeFnAttr, files, rejFiles, evt);
  200. if (files.length == 0) evt.target.value = files;
  201. // if (evt.target && evt.target.getAttribute('__ngf_gen__')) {
  202. // angular.element(evt.target).remove();
  203. // }
  204. } finally {
  205. isUpdating = false;
  206. }
  207. }
  208. }
  209. function bindAttrToFileInput(fileElem) {
  210. if (attr.ngfMultiple) fileElem.attr('multiple', $parse(attr.ngfMultiple)(scope));
  211. if (!$parse(attr.ngfMultiple)(scope)) fileElem.attr('multiple', undefined);
  212. if (attr['accept']) fileElem.attr('accept', attr['accept']);
  213. if (attr.ngfCapture) fileElem.attr('capture', $parse(attr.ngfCapture)(scope));
  214. if (attr.ngfDisabled) fileElem.attr('disabled', $parse(attr.ngfDisabled)(scope));
  215. for (var i = 0; i < elem[0].attributes.length; i++) {
  216. var attribute = elem[0].attributes[i];
  217. if (attribute.name !== 'type' && attribute.name !== 'class' && attribute.name !== 'id' && attribute.name !== 'style') {
  218. fileElem.attr(attribute.name, attribute.value);
  219. }
  220. }
  221. }
  222. function createFileInput(evt) {
  223. if (elem.attr('disabled')) {
  224. return;
  225. }
  226. var fileElem = angular.element('<input type="file">');
  227. bindAttrToFileInput(fileElem);
  228. if (isInputTypeFile()) {
  229. elem.replaceWith(fileElem);
  230. elem = fileElem;
  231. } else {
  232. fileElem.css('display', 'none').attr('tabindex', '-1').attr('__ngf_gen__', true);
  233. if (elem.__ngf_ref_elem__) {elem.__ngf_ref_elem__.remove();}
  234. elem.__ngf_ref_elem__ = fileElem;
  235. document.body.appendChild(fileElem[0]);
  236. }
  237. return fileElem;
  238. }
  239. function resetModel(evt) {
  240. updateModel($parse, $timeout, scope, ngModel, attr, changeFnAttr, [], [], evt, true);
  241. }
  242. function clickHandler(evt) {
  243. evt.preventDefault();
  244. var fileElem = createFileInput(evt);
  245. if (fileElem) {
  246. fileElem.bind('change', changeFn);
  247. resetModel(evt);
  248. function clickAndAssign() {
  249. fileElem[0].click();
  250. if (isInputTypeFile()) {
  251. elem.bind('click touchend', clickHandler);
  252. evt.preventDefault()
  253. }
  254. }
  255. // fix for android native browser
  256. if (navigator.userAgent.toLowerCase().match(/android/)) {
  257. setTimeout(function() {
  258. clickAndAssign();
  259. }, 0);
  260. } else {
  261. clickAndAssign();
  262. }
  263. }
  264. }
  265. if (window.FileAPI && window.FileAPI.ngfFixIE) {
  266. window.FileAPI.ngfFixIE(elem, createFileInput, bindAttrToFileInput, changeFn, resetModel);
  267. } else {
  268. elem.bind('click touchend', clickHandler);
  269. }
  270. }
  271. ngFileUpload.directive('ngfDrop', ['$parse', '$timeout', '$location', function ($parse, $timeout, $location) {
  272. return {
  273. restrict: 'AEC',
  274. require: '?ngModel',
  275. link: function (scope, elem, attr, ngModel) {
  276. linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $location);
  277. }
  278. }
  279. }]);
  280. ngFileUpload.directive('ngfNoFileDrop', function () {
  281. return function (scope, elem) {
  282. if (dropAvailable()) elem.css('display', 'none')
  283. }
  284. });
  285. ngFileUpload.directive('ngfDropAvailable', ['$parse', '$timeout', function ($parse, $timeout) {
  286. return function (scope, elem, attr) {
  287. if (dropAvailable()) {
  288. var fn = $parse(attr.ngfDropAvailable);
  289. $timeout(function () {
  290. fn(scope);
  291. if (fn.assign) {
  292. fn.assign(scope, true);
  293. }
  294. });
  295. }
  296. }
  297. }]);
  298. function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $location) {
  299. var available = dropAvailable();
  300. if (attr.dropAvailable) {
  301. $timeout(function () {
  302. scope[attr.dropAvailable] ? scope[attr.dropAvailable].value = available : scope[attr.dropAvailable] = available;
  303. });
  304. }
  305. if (!available) {
  306. if ($parse(attr.ngfHideOnDropNotAvailable)(scope) == true) {
  307. elem.css('display', 'none');
  308. }
  309. return;
  310. }
  311. var leaveTimeout = null;
  312. var stopPropagation = $parse(attr.ngfStopPropagation);
  313. var dragOverDelay = 1;
  314. var accept = $parse(attr.ngfAccept);
  315. var disabled = $parse(attr.ngfDisabled);
  316. var actualDragOverClass;
  317. elem[0].addEventListener('dragover', function (evt) {
  318. if (disabled(scope)) return;
  319. evt.preventDefault();
  320. if (stopPropagation(scope)) evt.stopPropagation();
  321. // handling dragover events from the Chrome download bar
  322. if (navigator.userAgent.indexOf("Chrome") > -1) {
  323. var b = evt.dataTransfer.effectAllowed;
  324. evt.dataTransfer.dropEffect = ('move' === b || 'linkMove' === b) ? 'move' : 'copy';
  325. }
  326. $timeout.cancel(leaveTimeout);
  327. if (!scope.actualDragOverClass) {
  328. actualDragOverClass = calculateDragOverClass(scope, attr, evt);
  329. }
  330. elem.addClass(actualDragOverClass);
  331. }, false);
  332. elem[0].addEventListener('dragenter', function (evt) {
  333. if (disabled(scope)) return;
  334. evt.preventDefault();
  335. if (stopPropagation(scope)) evt.stopPropagation();
  336. }, false);
  337. elem[0].addEventListener('dragleave', function () {
  338. if (disabled(scope)) return;
  339. leaveTimeout = $timeout(function () {
  340. elem.removeClass(actualDragOverClass);
  341. actualDragOverClass = null;
  342. }, dragOverDelay || 1);
  343. }, false);
  344. elem[0].addEventListener('drop', function (evt) {
  345. if (disabled(scope)) return;
  346. evt.preventDefault();
  347. if (stopPropagation(scope)) evt.stopPropagation();
  348. elem.removeClass(actualDragOverClass);
  349. actualDragOverClass = null;
  350. extractFiles(evt, function (files, rejFiles) {
  351. updateModel($parse, $timeout, scope, ngModel, attr,
  352. attr.ngfChange || (attr.ngfDrop && attr.ngfDrop.indexOf('(') > 0), files, rejFiles, evt)
  353. }, $parse(attr.ngfAllowDir)(scope) != false, attr.multiple || $parse(attr.ngfMultiple)(scope));
  354. }, false);
  355. function calculateDragOverClass(scope, attr, evt) {
  356. var accepted = true;
  357. var items = evt.dataTransfer.items;
  358. if (items != null) {
  359. for (var i = 0; i < items.length && accepted; i++) {
  360. accepted = accepted
  361. && (items[i].kind == 'file' || items[i].kind == '')
  362. && validate(scope, $parse, attr, items[i], evt);
  363. }
  364. }
  365. var clazz = $parse(attr.ngfDragOverClass)(scope, {$event: evt});
  366. if (clazz) {
  367. if (clazz.delay) dragOverDelay = clazz.delay;
  368. if (clazz.accept) clazz = accepted ? clazz.accept : clazz.reject;
  369. }
  370. return clazz || attr.ngfDragOverClass || 'dragover';
  371. }
  372. function extractFiles(evt, callback, allowDir, multiple) {
  373. var files = [], rejFiles = [], items = evt.dataTransfer.items, processing = 0;
  374. function addFile(file) {
  375. if (validate(scope, $parse, attr, file, evt)) {
  376. files.push(file);
  377. } else {
  378. rejFiles.push(file);
  379. }
  380. }
  381. if (items && items.length > 0 && $location.protocol() != 'file') {
  382. for (var i = 0; i < items.length; i++) {
  383. if (items[i].webkitGetAsEntry && items[i].webkitGetAsEntry() && items[i].webkitGetAsEntry().isDirectory) {
  384. var entry = items[i].webkitGetAsEntry();
  385. if (entry.isDirectory && !allowDir) {
  386. continue;
  387. }
  388. if (entry != null) {
  389. traverseFileTree(files, entry);
  390. }
  391. } else {
  392. var f = items[i].getAsFile();
  393. if (f != null) addFile(f);
  394. }
  395. if (!multiple && files.length > 0) break;
  396. }
  397. } else {
  398. var fileList = evt.dataTransfer.files;
  399. if (fileList != null) {
  400. for (var i = 0; i < fileList.length; i++) {
  401. addFile(fileList.item(i));
  402. if (!multiple && files.length > 0) break;
  403. }
  404. }
  405. }
  406. var delays = 0;
  407. (function waitForProcess(delay) {
  408. $timeout(function () {
  409. if (!processing) {
  410. if (!multiple && files.length > 1) {
  411. i = 0;
  412. while (files[i].type == 'directory') i++;
  413. files = [files[i]];
  414. }
  415. callback(files, rejFiles);
  416. } else {
  417. if (delays++ * 10 < 20 * 1000) {
  418. waitForProcess(10);
  419. }
  420. }
  421. }, delay || 0)
  422. })();
  423. function traverseFileTree(files, entry, path) {
  424. if (entry != null) {
  425. if (entry.isDirectory) {
  426. var filePath = (path || '') + entry.name;
  427. addFile({name: entry.name, type: 'directory', path: filePath});
  428. var dirReader = entry.createReader();
  429. var entries = [];
  430. processing++;
  431. var readEntries = function () {
  432. dirReader.readEntries(function (results) {
  433. try {
  434. if (!results.length) {
  435. for (var i = 0; i < entries.length; i++) {
  436. traverseFileTree(files, entries[i], (path ? path : '') + entry.name + '/');
  437. }
  438. processing--;
  439. } else {
  440. entries = entries.concat(Array.prototype.slice.call(results || [], 0));
  441. readEntries();
  442. }
  443. } catch (e) {
  444. processing--;
  445. console.error(e);
  446. }
  447. }, function () {
  448. processing--;
  449. });
  450. };
  451. readEntries();
  452. } else {
  453. processing++;
  454. entry.file(function (file) {
  455. try {
  456. processing--;
  457. file.path = (path ? path : '') + file.name;
  458. addFile(file);
  459. } catch (e) {
  460. processing--;
  461. console.error(e);
  462. }
  463. }, function () {
  464. processing--;
  465. });
  466. }
  467. }
  468. }
  469. }
  470. }
  471. ngFileUpload.directive('ngfSrc', ['$parse', '$timeout', function ($parse, $timeout) {
  472. return {
  473. restrict: 'AE',
  474. link: function (scope, elem, attr, file) {
  475. if (window.FileReader) {
  476. scope.$watch(attr.ngfSrc, function(file) {
  477. if (file) {
  478. $timeout(function() {
  479. var fileReader = new FileReader();
  480. fileReader.readAsDataURL(file);
  481. fileReader.onload = function(e) {
  482. $timeout(function() {
  483. elem.attr('src', e.target.result);
  484. });
  485. }
  486. });
  487. } else {
  488. elem.attr('src', '');
  489. }
  490. });
  491. }
  492. }
  493. }
  494. }]);
  495. function dropAvailable() {
  496. var div = document.createElement('div');
  497. return ('draggable' in div) && ('ondrop' in div);
  498. }
  499. function updateModel($parse, $timeout, scope, ngModel, attr, fileChange, files, rejFiles, evt, noDelay) {
  500. function update() {
  501. if (ngModel) {
  502. $parse(attr.ngModel).assign(scope, files);
  503. $timeout(function () {
  504. ngModel && ngModel.$setViewValue(files != null && files.length == 0 ? null : files);
  505. });
  506. }
  507. if (attr.ngModelRejected) {
  508. $parse(attr.ngModelRejected).assign(scope, rejFiles);
  509. }
  510. if (fileChange) {
  511. $parse(fileChange)(scope, {
  512. $files: files,
  513. $rejectedFiles: rejFiles,
  514. $event: evt
  515. });
  516. }
  517. }
  518. if (noDelay) {
  519. update();
  520. } else {
  521. $timeout(function () {
  522. update();
  523. });
  524. }
  525. }
  526. function validate(scope, $parse, attr, file, evt) {
  527. var accept = $parse(attr.ngfAccept);
  528. var fileSizeMax = $parse(attr.ngfMaxSize)(scope) || 9007199254740991;
  529. var fileSizeMin = $parse(attr.ngfMinSize)(scope) || -1;
  530. var val = accept(scope, {$file: file, $event: evt}), match = false;
  531. if (val != null && angular.isString(val)) {
  532. var regexp = new RegExp(globStringToRegex(val), 'gi');
  533. match = (file.type != null && file.type.match(regexp)) ||
  534. (file.name != null && file.name.match(regexp));
  535. }
  536. return (val == null || match) && (file.size == null || (file.size < fileSizeMax && file.size > fileSizeMin));
  537. }
  538. function globStringToRegex(str) {
  539. if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') {
  540. return str.substring(1, str.length - 1);
  541. }
  542. var split = str.split(','), result = '';
  543. if (split.length > 1) {
  544. for (var i = 0; i < split.length; i++) {
  545. result += '(' + globStringToRegex(split[i]) + ')';
  546. if (i < split.length - 1) {
  547. result += '|'
  548. }
  549. }
  550. } else {
  551. if (str.indexOf('.') == 0) {
  552. str = '*' + str;
  553. }
  554. result = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + '-]', 'g'), '\\$&') + '$';
  555. result = result.replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
  556. }
  557. return result;
  558. }
  559. })();
  560. /**!
  561. * AngularJS file upload/drop directive and service with progress and abort
  562. * FileAPI Flash shim for old browsers not supporting FormData
  563. * @author Danial <danial.farid@gmail.com>
  564. * @version 4.1.0
  565. */
  566. (function() {
  567. var hasFlash = function() {
  568. try {
  569. var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
  570. if (fo) return true;
  571. } catch(e) {
  572. if (navigator.mimeTypes['application/x-shockwave-flash'] != undefined) return true;
  573. }
  574. return false;
  575. }
  576. function patchXHR(fnName, newFn) {
  577. window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
  578. };
  579. if ((window.XMLHttpRequest && !window.FormData) || (window.FileAPI && FileAPI.forceLoad)) {
  580. var initializeUploadListener = function(xhr) {
  581. if (!xhr.__listeners) {
  582. if (!xhr.upload) xhr.upload = {};
  583. xhr.__listeners = [];
  584. var origAddEventListener = xhr.upload.addEventListener;
  585. xhr.upload.addEventListener = function(t, fn, b) {
  586. xhr.__listeners[t] = fn;
  587. origAddEventListener && origAddEventListener.apply(this, arguments);
  588. };
  589. }
  590. }
  591. patchXHR('open', function(orig) {
  592. return function(m, url, b) {
  593. initializeUploadListener(this);
  594. this.__url = url;
  595. try {
  596. orig.apply(this, [m, url, b]);
  597. } catch (e) {
  598. if (e.message.indexOf('Access is denied') > -1) {
  599. this.__origError = e;
  600. orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]);
  601. }
  602. }
  603. }
  604. });
  605. patchXHR('getResponseHeader', function(orig) {
  606. return function(h) {
  607. return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h]));
  608. };
  609. });
  610. patchXHR('getAllResponseHeaders', function(orig) {
  611. return function() {
  612. return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this));
  613. }
  614. });
  615. patchXHR('abort', function(orig) {
  616. return function() {
  617. return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this));
  618. }
  619. });
  620. patchXHR('setRequestHeader', function(orig) {
  621. return function(header, value) {
  622. if (header === '__setXHR_') {
  623. initializeUploadListener(this);
  624. var val = value(this);
  625. // fix for angular < 1.2.0
  626. if (val instanceof Function) {
  627. val(this);
  628. }
  629. } else {
  630. this.__requestHeaders = this.__requestHeaders || {};
  631. this.__requestHeaders[header] = value;
  632. orig.apply(this, arguments);
  633. }
  634. }
  635. });
  636. function redefineProp(xhr, prop, fn) {
  637. try {
  638. Object.defineProperty(xhr, prop, {get: fn});
  639. } catch (e) {/*ignore*/}
  640. }
  641. patchXHR('send', function(orig) {
  642. return function() {
  643. var xhr = this;
  644. if (arguments[0] && arguments[0].__isFileAPIShim) {
  645. var formData = arguments[0];
  646. var config = {
  647. url: xhr.__url,
  648. jsonp: false, //removes the callback form param
  649. cache: true, //removes the ?fileapiXXX in the url
  650. complete: function(err, fileApiXHR) {
  651. xhr.__completed = true;
  652. if (!err && xhr.__listeners['load'])
  653. xhr.__listeners['load']({type: 'load', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
  654. if (!err && xhr.__listeners['loadend'])
  655. xhr.__listeners['loadend']({type: 'loadend', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
  656. if (err === 'abort' && xhr.__listeners['abort'])
  657. xhr.__listeners['abort']({type: 'abort', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
  658. if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function() {return (fileApiXHR.status == 0 && err && err !== 'abort') ? 500 : fileApiXHR.status});
  659. if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function() {return fileApiXHR.statusText});
  660. redefineProp(xhr, 'readyState', function() {return 4});
  661. if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function() {return fileApiXHR.response});
  662. var resp = fileApiXHR.responseText || (err && fileApiXHR.status == 0 && err !== 'abort' ? err : undefined);
  663. redefineProp(xhr, 'responseText', function() {return resp});
  664. redefineProp(xhr, 'response', function() {return resp});
  665. if (err) redefineProp(xhr, 'err', function() {return err});
  666. xhr.__fileApiXHR = fileApiXHR;
  667. if (xhr.onreadystatechange) xhr.onreadystatechange();
  668. if (xhr.onload) xhr.onload();
  669. },
  670. fileprogress: function(e) {
  671. e.target = xhr;
  672. xhr.__listeners['progress'] && xhr.__listeners['progress'](e);
  673. xhr.__total = e.total;
  674. xhr.__loaded = e.loaded;
  675. if (e.total === e.loaded) {
  676. // fix flash issue that doesn't call complete if there is no response text from the server
  677. var _this = this
  678. setTimeout(function() {
  679. if (!xhr.__completed) {
  680. xhr.getAllResponseHeaders = function(){};
  681. _this.complete(null, {status: 204, statusText: 'No Content'});
  682. }
  683. }, FileAPI.noContentTimeout || 10000);
  684. }
  685. },
  686. headers: xhr.__requestHeaders
  687. }
  688. config.data = {};
  689. config.files = {}
  690. for (var i = 0; i < formData.data.length; i++) {
  691. var item = formData.data[i];
  692. if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) {
  693. config.files[item.key] = item.val;
  694. } else {
  695. config.data[item.key] = item.val;
  696. }
  697. }
  698. setTimeout(function() {
  699. if (!hasFlash()) {
  700. throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
  701. }
  702. xhr.__fileApiXHR = FileAPI.upload(config);
  703. }, 1);
  704. } else {
  705. if (this.__origError) {
  706. throw this.__origError;
  707. }
  708. orig.apply(xhr, arguments);
  709. }
  710. }
  711. });
  712. window.XMLHttpRequest.__isFileAPIShim = true;
  713. function isInputTypeFile(elem) {
  714. return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file';
  715. }
  716. window.FormData = FormData = function() {
  717. return {
  718. append: function(key, val, name) {
  719. if (val.__isFileAPIBlobShim) {
  720. val = val.data[0];
  721. }
  722. this.data.push({
  723. key: key,
  724. val: val,
  725. name: name
  726. });
  727. },
  728. data: [],
  729. __isFileAPIShim: true
  730. };
  731. };
  732. window.Blob = Blob = function(b) {
  733. return {
  734. data: b,
  735. __isFileAPIBlobShim: true
  736. };
  737. };
  738. (function () {
  739. //load FileAPI
  740. if (!window.FileAPI) {
  741. window.FileAPI = {};
  742. }
  743. if (FileAPI.forceLoad) {
  744. FileAPI.html5 = false;
  745. }
  746. if (!FileAPI.upload) {
  747. var jsUrl, basePath, script = document.createElement('script'), allScripts = document.getElementsByTagName('script'), i, index, src;
  748. if (window.FileAPI.jsUrl) {
  749. jsUrl = window.FileAPI.jsUrl;
  750. } else if (window.FileAPI.jsPath) {
  751. basePath = window.FileAPI.jsPath;
  752. } else {
  753. for (i = 0; i < allScripts.length; i++) {
  754. src = allScripts[i].src;
  755. index = src.search(/\/ng\-file\-upload[\-a-zA-z0-9\.]*\.js/)
  756. if (index > -1) {
  757. basePath = src.substring(0, index + 1);
  758. break;
  759. }
  760. }
  761. }
  762. if (FileAPI.staticPath == null) FileAPI.staticPath = basePath;
  763. script.setAttribute('src', jsUrl || basePath + 'FileAPI.min.js');
  764. document.getElementsByTagName('head')[0].appendChild(script);
  765. FileAPI.hasFlash = hasFlash();
  766. }
  767. })();
  768. FileAPI.ngfFixIE = function(elem, createFileElemFn, bindAttr, changeFn, resetModel) {
  769. if (!hasFlash()) {
  770. throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
  771. }
  772. var makeFlashInput = function(evt) {
  773. if (elem.attr('disabled')) {
  774. elem.__ngf_elem__.removeClass('js-fileapi-wrapper');
  775. } else {
  776. var fileElem = elem.__ngf_elem__;
  777. if (!fileElem) {
  778. fileElem = elem.__ngf_elem__ = createFileElemFn();
  779. fileElem.addClass('js-fileapi-wrapper');
  780. if (!isInputTypeFile(elem)) {
  781. // if (fileElem.parent().css('position') === '' || fileElem.parent().css('position') === 'static') {
  782. // fileElem.parent().css('position', 'relative');
  783. // }
  784. // elem.parent()[0].insertBefore(fileElem[0], elem[0]);
  785. // elem.css('overflow', 'hidden');
  786. }
  787. setTimeout(function() {
  788. fileElem.bind('mouseenter', makeFlashInput);
  789. }, 10);
  790. fileElem.bind('change', function(evt) {
  791. fileApiChangeFn.apply(this, [evt]);
  792. changeFn.apply(this, [evt]);
  793. // alert('change' + evt);
  794. });
  795. } else {
  796. bindAttr(elem.__ngf_elem__);
  797. }
  798. if (!isInputTypeFile(elem)) {
  799. fileElem.css('position', 'absolute')
  800. .css('top', getOffset(elem[0]).top + 'px').css('left', getOffset(elem[0]).left + 'px')
  801. .css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px')
  802. .css('filter', 'alpha(opacity=0)').css('display', elem.css('display'))
  803. .css('overflow', 'hidden').css('z-index', '900000');
  804. }
  805. }
  806. function getOffset(obj) {
  807. var left, top;
  808. left = top = 0;
  809. if (obj.offsetParent) {
  810. do {
  811. left += obj.offsetLeft;
  812. top += obj.offsetTop;
  813. } while (obj = obj.offsetParent);
  814. }
  815. return {
  816. left : left,
  817. top : top
  818. };
  819. };
  820. };
  821. elem.bind('mouseenter', makeFlashInput);
  822. var fileApiChangeFn = function(evt) {
  823. var files = FileAPI.getFiles(evt);
  824. //just a double check for #233
  825. for (var i = 0; i < files.length; i++) {
  826. if (files[i].size === undefined) files[i].size = 0;
  827. if (files[i].name === undefined) files[i].name = 'file';
  828. if (files[i].type === undefined) files[i].type = 'undefined';
  829. }
  830. if (!evt.target) {
  831. evt.target = {};
  832. }
  833. evt.target.files = files;
  834. // if evt.target.files is not writable use helper field
  835. if (evt.target.files != files) {
  836. evt.__files_ = files;
  837. }
  838. (evt.__files_ || evt.target.files).item = function(i) {
  839. return (evt.__files_ || evt.target.files)[i] || null;
  840. };
  841. };
  842. };
  843. FileAPI.disableFileInput = function(elem, disable) {
  844. if (disable) {
  845. elem.removeClass('js-fileapi-wrapper')
  846. } else {
  847. elem.addClass('js-fileapi-wrapper');
  848. }
  849. };
  850. }
  851. if (!window.FileReader) {
  852. window.FileReader = function() {
  853. var _this = this, loadStarted = false;
  854. this.listeners = {};
  855. this.addEventListener = function(type, fn) {
  856. _this.listeners[type] = _this.listeners[type] || [];
  857. _this.listeners[type].push(fn);
  858. };
  859. this.removeEventListener = function(type, fn) {
  860. _this.listeners[type] && _this.listeners[type].splice(_this.listeners[type].indexOf(fn), 1);
  861. };
  862. this.dispatchEvent = function(evt) {
  863. var list = _this.listeners[evt.type];
  864. if (list) {
  865. for (var i = 0; i < list.length; i++) {
  866. list[i].call(_this, evt);
  867. }
  868. }
  869. };
  870. this.onabort = this.onerror = this.onload = this.onloadstart = this.onloadend = this.onprogress = null;
  871. var constructEvent = function(type, evt) {
  872. var e = {type: type, target: _this, loaded: evt.loaded, total: evt.total, error: evt.error};
  873. if (evt.result != null) e.target.result = evt.result;
  874. return e;
  875. };
  876. var listener = function(evt) {
  877. if (!loadStarted) {
  878. loadStarted = true;
  879. _this.onloadstart && _this.onloadstart(constructEvent('loadstart', evt));
  880. }
  881. if (evt.type === 'load') {
  882. _this.onloadend && _this.onloadend(constructEvent('loadend', evt));
  883. var e = constructEvent('load', evt);
  884. _this.onload && _this.onload(e);
  885. _this.dispatchEvent(e);
  886. } else if (evt.type === 'progress') {
  887. var e = constructEvent('progress', evt);
  888. _this.onprogress && _this.onprogress(e);
  889. _this.dispatchEvent(e);
  890. } else {
  891. var e = constructEvent('error', evt);
  892. _this.onerror && _this.onerror(e);
  893. _this.dispatchEvent(e);
  894. }
  895. };
  896. this.readAsArrayBuffer = function(file) {
  897. FileAPI.readAsBinaryString(file, listener);
  898. }
  899. this.readAsBinaryString = function(file) {
  900. FileAPI.readAsBinaryString(file, listener);
  901. }
  902. this.readAsDataURL = function(file) {
  903. FileAPI.readAsDataURL(file, listener);
  904. }
  905. this.readAsText = function(file) {
  906. FileAPI.readAsText(file, listener);
  907. }
  908. }
  909. }
  910. })();