@@ 1626-2126 (lines=501) @@ | ||
1623 | ||
1624 | })(); |
|
1625 | ||
1626 | ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) { |
|
1627 | var upload = UploadDataUrl; |
|
1628 | ||
1629 | function globStringToRegex(str) { |
|
1630 | var regexp = '', excludes = []; |
|
1631 | if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { |
|
1632 | regexp = str.substring(1, str.length - 1); |
|
1633 | } else { |
|
1634 | var split = str.split(','); |
|
1635 | if (split.length > 1) { |
|
1636 | for (var i = 0; i < split.length; i++) { |
|
1637 | var r = globStringToRegex(split[i]); |
|
1638 | if (r.regexp) { |
|
1639 | regexp += '(' + r.regexp + ')'; |
|
1640 | if (i < split.length - 1) { |
|
1641 | regexp += '|'; |
|
1642 | } |
|
1643 | } else { |
|
1644 | excludes = excludes.concat(r.excludes); |
|
1645 | } |
|
1646 | } |
|
1647 | } else { |
|
1648 | if (str.indexOf('!') === 0) { |
|
1649 | excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$'); |
|
1650 | } else { |
|
1651 | if (str.indexOf('.') === 0) { |
|
1652 | str = '*' + str; |
|
1653 | } |
|
1654 | regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$'; |
|
1655 | regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); |
|
1656 | } |
|
1657 | } |
|
1658 | } |
|
1659 | return {regexp: regexp, excludes: excludes}; |
|
1660 | } |
|
1661 | ||
1662 | upload.validatePattern = function (file, val) { |
|
1663 | if (!val) { |
|
1664 | return true; |
|
1665 | } |
|
1666 | var pattern = globStringToRegex(val), valid = true; |
|
1667 | if (pattern.regexp && pattern.regexp.length) { |
|
1668 | var regexp = new RegExp(pattern.regexp, 'i'); |
|
1669 | valid = (file.type != null && regexp.test(file.type)) || |
|
1670 | (file.name != null && regexp.test(file.name)); |
|
1671 | } |
|
1672 | var len = pattern.excludes.length; |
|
1673 | while (len--) { |
|
1674 | var exclude = new RegExp(pattern.excludes[len], 'i'); |
|
1675 | valid = valid && (file.type == null || exclude.test(file.type)) && |
|
1676 | (file.name == null || exclude.test(file.name)); |
|
1677 | } |
|
1678 | return valid; |
|
1679 | }; |
|
1680 | ||
1681 | upload.ratioToFloat = function (val) { |
|
1682 | var r = val.toString(), xIndex = r.search(/[x:]/i); |
|
1683 | if (xIndex > -1) { |
|
1684 | r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1)); |
|
1685 | } else { |
|
1686 | r = parseFloat(r); |
|
1687 | } |
|
1688 | return r; |
|
1689 | }; |
|
1690 | ||
1691 | upload.registerModelChangeValidator = function (ngModel, attr, scope) { |
|
1692 | if (ngModel) { |
|
1693 | ngModel.$formatters.push(function (files) { |
|
1694 | if (ngModel.$dirty) { |
|
1695 | var filesArray = files; |
|
1696 | if (files && !angular.isArray(files)) { |
|
1697 | filesArray = [files]; |
|
1698 | } |
|
1699 | upload.validate(filesArray, 0, ngModel, attr, scope).then(function () { |
|
1700 | upload.applyModelValidation(ngModel, filesArray); |
|
1701 | }); |
|
1702 | } |
|
1703 | return files; |
|
1704 | }); |
|
1705 | } |
|
1706 | }; |
|
1707 | ||
1708 | function markModelAsDirty(ngModel, files) { |
|
1709 | if (files != null && !ngModel.$dirty) { |
|
1710 | if (ngModel.$setDirty) { |
|
1711 | ngModel.$setDirty(); |
|
1712 | } else { |
|
1713 | ngModel.$dirty = true; |
|
1714 | } |
|
1715 | } |
|
1716 | } |
|
1717 | ||
1718 | upload.applyModelValidation = function (ngModel, files) { |
|
1719 | markModelAsDirty(ngModel, files); |
|
1720 | angular.forEach(ngModel.$ngfValidations, function (validation) { |
|
1721 | ngModel.$setValidity(validation.name, validation.valid); |
|
1722 | }); |
|
1723 | }; |
|
1724 | ||
1725 | upload.getValidationAttr = function (attr, scope, name, validationName, file) { |
|
1726 | var dName = 'ngf' + name[0].toUpperCase() + name.substr(1); |
|
1727 | var val = upload.attrGetter(dName, attr, scope, {$file: file}); |
|
1728 | if (val == null) { |
|
1729 | val = upload.attrGetter('ngfValidate', attr, scope, {$file: file}); |
|
1730 | if (val) { |
|
1731 | var split = (validationName || name).split('.'); |
|
1732 | val = val[split[0]]; |
|
1733 | if (split.length > 1) { |
|
1734 | val = val && val[split[1]]; |
|
1735 | } |
|
1736 | } |
|
1737 | } |
|
1738 | return val; |
|
1739 | }; |
|
1740 | ||
1741 | upload.validate = function (files, prevLength, ngModel, attr, scope) { |
|
1742 | ngModel = ngModel || {}; |
|
1743 | ngModel.$ngfValidations = ngModel.$ngfValidations || []; |
|
1744 | ||
1745 | angular.forEach(ngModel.$ngfValidations, function (v) { |
|
1746 | v.valid = true; |
|
1747 | }); |
|
1748 | ||
1749 | var attrGetter = function (name, params) { |
|
1750 | return upload.attrGetter(name, attr, scope, params); |
|
1751 | }; |
|
1752 | ||
1753 | var ignoredErrors = (upload.attrGetter('ngfIgnoreInvalid', attr, scope) || '').split(' '); |
|
1754 | var runAllValidation = upload.attrGetter('ngfRunAllValidations', attr, scope); |
|
1755 | ||
1756 | if (files == null || files.length === 0) { |
|
1757 | return upload.emptyPromise({'validFiles': files, 'invalidFiles': []}); |
|
1758 | } |
|
1759 | ||
1760 | files = files.length === undefined ? [files] : files.slice(0); |
|
1761 | var invalidFiles = []; |
|
1762 | ||
1763 | function validateSync(name, validationName, fn) { |
|
1764 | if (files) { |
|
1765 | var i = files.length, valid = null; |
|
1766 | while (i--) { |
|
1767 | var file = files[i]; |
|
1768 | if (file) { |
|
1769 | var val = upload.getValidationAttr(attr, scope, name, validationName, file); |
|
1770 | if (val != null) { |
|
1771 | if (!fn(file, val, i)) { |
|
1772 | if (ignoredErrors.indexOf(name) === -1) { |
|
1773 | file.$error = name; |
|
1774 | (file.$errorMessages = (file.$errorMessages || {}))[name] = true; |
|
1775 | file.$errorParam = val; |
|
1776 | if (invalidFiles.indexOf(file) === -1) { |
|
1777 | invalidFiles.push(file); |
|
1778 | } |
|
1779 | if (!runAllValidation) { |
|
1780 | files.splice(i, 1); |
|
1781 | } |
|
1782 | valid = false; |
|
1783 | } else { |
|
1784 | files.splice(i, 1); |
|
1785 | } |
|
1786 | } |
|
1787 | } |
|
1788 | } |
|
1789 | } |
|
1790 | if (valid !== null) { |
|
1791 | ngModel.$ngfValidations.push({name: name, valid: valid}); |
|
1792 | } |
|
1793 | } |
|
1794 | } |
|
1795 | ||
1796 | validateSync('pattern', null, upload.validatePattern); |
|
1797 | validateSync('minSize', 'size.min', function (file, val) { |
|
1798 | return file.size + 0.1 >= upload.translateScalars(val); |
|
1799 | }); |
|
1800 | validateSync('maxSize', 'size.max', function (file, val) { |
|
1801 | return file.size - 0.1 <= upload.translateScalars(val); |
|
1802 | }); |
|
1803 | var totalSize = 0; |
|
1804 | validateSync('maxTotalSize', null, function (file, val) { |
|
1805 | totalSize += file.size; |
|
1806 | if (totalSize > upload.translateScalars(val)) { |
|
1807 | files.splice(0, files.length); |
|
1808 | return false; |
|
1809 | } |
|
1810 | return true; |
|
1811 | }); |
|
1812 | ||
1813 | validateSync('validateFn', null, function (file, r) { |
|
1814 | return r === true || r === null || r === ''; |
|
1815 | }); |
|
1816 | ||
1817 | if (!files.length) { |
|
1818 | return upload.emptyPromise({'validFiles': [], 'invalidFiles': invalidFiles}); |
|
1819 | } |
|
1820 | ||
1821 | function validateAsync(name, validationName, type, asyncFn, fn) { |
|
1822 | function resolveResult(defer, file, val) { |
|
1823 | function resolveInternal(fn) { |
|
1824 | if (fn()) { |
|
1825 | if (ignoredErrors.indexOf(name) === -1) { |
|
1826 | file.$error = name; |
|
1827 | (file.$errorMessages = (file.$errorMessages || {}))[name] = true; |
|
1828 | file.$errorParam = val; |
|
1829 | if (invalidFiles.indexOf(file) === -1) { |
|
1830 | invalidFiles.push(file); |
|
1831 | } |
|
1832 | if (!runAllValidation) { |
|
1833 | var i = files.indexOf(file); |
|
1834 | if (i > -1) files.splice(i, 1); |
|
1835 | } |
|
1836 | defer.resolve(false); |
|
1837 | } else { |
|
1838 | var j = files.indexOf(file); |
|
1839 | if (j > -1) files.splice(j, 1); |
|
1840 | defer.resolve(true); |
|
1841 | } |
|
1842 | } else { |
|
1843 | defer.resolve(true); |
|
1844 | } |
|
1845 | } |
|
1846 | ||
1847 | if (val != null) { |
|
1848 | asyncFn(file, val).then(function (d) { |
|
1849 | resolveInternal(function () { |
|
1850 | return !fn(d, val); |
|
1851 | }); |
|
1852 | }, function () { |
|
1853 | resolveInternal(function () { |
|
1854 | return attrGetter('ngfValidateForce', {$file: file}); |
|
1855 | }); |
|
1856 | }); |
|
1857 | } else { |
|
1858 | defer.resolve(true); |
|
1859 | } |
|
1860 | } |
|
1861 | ||
1862 | var promises = [upload.emptyPromise(true)]; |
|
1863 | if (files) { |
|
1864 | files = files.length === undefined ? [files] : files; |
|
1865 | angular.forEach(files, function (file) { |
|
1866 | var defer = $q.defer(); |
|
1867 | promises.push(defer.promise); |
|
1868 | if (type && (file.type == null || file.type.search(type) !== 0)) { |
|
1869 | defer.resolve(true); |
|
1870 | return; |
|
1871 | } |
|
1872 | if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) { |
|
1873 | upload.imageDimensions(file).then(function (d) { |
|
1874 | resolveResult(defer, file, |
|
1875 | attrGetter('ngfDimensions', {$file: file, $width: d.width, $height: d.height})); |
|
1876 | }, function () { |
|
1877 | defer.resolve(false); |
|
1878 | }); |
|
1879 | } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) { |
|
1880 | upload.mediaDuration(file).then(function (d) { |
|
1881 | resolveResult(defer, file, |
|
1882 | attrGetter('ngfDuration', {$file: file, $duration: d})); |
|
1883 | }, function () { |
|
1884 | defer.resolve(false); |
|
1885 | }); |
|
1886 | } else { |
|
1887 | resolveResult(defer, file, |
|
1888 | upload.getValidationAttr(attr, scope, name, validationName, file)); |
|
1889 | } |
|
1890 | }); |
|
1891 | } |
|
1892 | var deffer = $q.defer(); |
|
1893 | $q.all(promises).then(function (values) { |
|
1894 | var isValid = true; |
|
1895 | for (var i = 0; i < values.length; i++) { |
|
1896 | if (!values[i]) { |
|
1897 | isValid = false; |
|
1898 | break; |
|
1899 | } |
|
1900 | } |
|
1901 | ngModel.$ngfValidations.push({name: name, valid: isValid}); |
|
1902 | deffer.resolve(isValid); |
|
1903 | }); |
|
1904 | return deffer.promise; |
|
1905 | } |
|
1906 | ||
1907 | var deffer = $q.defer(); |
|
1908 | var promises = []; |
|
1909 | ||
1910 | promises.push(validateAsync('maxHeight', 'height.max', /image/, |
|
1911 | this.imageDimensions, function (d, val) { |
|
1912 | return d.height <= val; |
|
1913 | })); |
|
1914 | promises.push(validateAsync('minHeight', 'height.min', /image/, |
|
1915 | this.imageDimensions, function (d, val) { |
|
1916 | return d.height >= val; |
|
1917 | })); |
|
1918 | promises.push(validateAsync('maxWidth', 'width.max', /image/, |
|
1919 | this.imageDimensions, function (d, val) { |
|
1920 | return d.width <= val; |
|
1921 | })); |
|
1922 | promises.push(validateAsync('minWidth', 'width.min', /image/, |
|
1923 | this.imageDimensions, function (d, val) { |
|
1924 | return d.width >= val; |
|
1925 | })); |
|
1926 | promises.push(validateAsync('dimensions', null, /image/, |
|
1927 | function (file, val) { |
|
1928 | return upload.emptyPromise(val); |
|
1929 | }, function (r) { |
|
1930 | return r; |
|
1931 | })); |
|
1932 | promises.push(validateAsync('ratio', null, /image/, |
|
1933 | this.imageDimensions, function (d, val) { |
|
1934 | var split = val.toString().split(','), valid = false; |
|
1935 | for (var i = 0; i < split.length; i++) { |
|
1936 | if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.01) { |
|
1937 | valid = true; |
|
1938 | } |
|
1939 | } |
|
1940 | return valid; |
|
1941 | })); |
|
1942 | promises.push(validateAsync('maxRatio', 'ratio.max', /image/, |
|
1943 | this.imageDimensions, function (d, val) { |
|
1944 | return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001; |
|
1945 | })); |
|
1946 | promises.push(validateAsync('minRatio', 'ratio.min', /image/, |
|
1947 | this.imageDimensions, function (d, val) { |
|
1948 | return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001; |
|
1949 | })); |
|
1950 | promises.push(validateAsync('maxDuration', 'duration.max', /audio|video/, |
|
1951 | this.mediaDuration, function (d, val) { |
|
1952 | return d <= upload.translateScalars(val); |
|
1953 | })); |
|
1954 | promises.push(validateAsync('minDuration', 'duration.min', /audio|video/, |
|
1955 | this.mediaDuration, function (d, val) { |
|
1956 | return d >= upload.translateScalars(val); |
|
1957 | })); |
|
1958 | promises.push(validateAsync('duration', null, /audio|video/, |
|
1959 | function (file, val) { |
|
1960 | return upload.emptyPromise(val); |
|
1961 | }, function (r) { |
|
1962 | return r; |
|
1963 | })); |
|
1964 | ||
1965 | promises.push(validateAsync('validateAsyncFn', null, null, |
|
1966 | function (file, val) { |
|
1967 | return val; |
|
1968 | }, function (r) { |
|
1969 | return r === true || r === null || r === ''; |
|
1970 | })); |
|
1971 | ||
1972 | $q.all(promises).then(function () { |
|
1973 | ||
1974 | if (runAllValidation) { |
|
1975 | for (var i = 0; i < files.length; i++) { |
|
1976 | var file = files[i]; |
|
1977 | if (file.$error) { |
|
1978 | files.splice(i--, 1); |
|
1979 | } |
|
1980 | } |
|
1981 | } |
|
1982 | ||
1983 | runAllValidation = false; |
|
1984 | validateSync('maxFiles', null, function (file, val, i) { |
|
1985 | return prevLength + i < val; |
|
1986 | }); |
|
1987 | ||
1988 | deffer.resolve({'validFiles': files, 'invalidFiles': invalidFiles}); |
|
1989 | }); |
|
1990 | return deffer.promise; |
|
1991 | }; |
|
1992 | ||
1993 | upload.imageDimensions = function (file) { |
|
1994 | if (file.$ngfWidth && file.$ngfHeight) { |
|
1995 | var d = $q.defer(); |
|
1996 | $timeout(function () { |
|
1997 | d.resolve({width: file.$ngfWidth, height: file.$ngfHeight}); |
|
1998 | }); |
|
1999 | return d.promise; |
|
2000 | } |
|
2001 | if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise; |
|
2002 | ||
2003 | var deferred = $q.defer(); |
|
2004 | $timeout(function () { |
|
2005 | if (file.type.indexOf('image') !== 0) { |
|
2006 | deferred.reject('not image'); |
|
2007 | return; |
|
2008 | } |
|
2009 | upload.dataUrl(file).then(function (dataUrl) { |
|
2010 | var img = angular.element('<img>').attr('src', dataUrl) |
|
2011 | .css('visibility', 'hidden').css('position', 'fixed') |
|
2012 | .css('max-width', 'none !important').css('max-height', 'none !important'); |
|
2013 | ||
2014 | function success() { |
|
2015 | var width = img[0].naturalWidth || img[0].clientWidth; |
|
2016 | var height = img[0].naturalHeight || img[0].clientHeight; |
|
2017 | img.remove(); |
|
2018 | file.$ngfWidth = width; |
|
2019 | file.$ngfHeight = height; |
|
2020 | deferred.resolve({width: width, height: height}); |
|
2021 | } |
|
2022 | ||
2023 | function error() { |
|
2024 | img.remove(); |
|
2025 | deferred.reject('load error'); |
|
2026 | } |
|
2027 | ||
2028 | img.on('load', success); |
|
2029 | img.on('error', error); |
|
2030 | ||
2031 | var secondsCounter = 0; |
|
2032 | function checkLoadErrorInCaseOfNoCallback() { |
|
2033 | $timeout(function () { |
|
2034 | if (img[0].parentNode) { |
|
2035 | if (img[0].clientWidth) { |
|
2036 | success(); |
|
2037 | } else if (secondsCounter++ > 10) { |
|
2038 | error(); |
|
2039 | } else { |
|
2040 | checkLoadErrorInCaseOfNoCallback(); |
|
2041 | } |
|
2042 | } |
|
2043 | }, 1000); |
|
2044 | } |
|
2045 | ||
2046 | checkLoadErrorInCaseOfNoCallback(); |
|
2047 | ||
2048 | angular.element(document.getElementsByTagName('body')[0]).append(img); |
|
2049 | }, function () { |
|
2050 | deferred.reject('load error'); |
|
2051 | }); |
|
2052 | }); |
|
2053 | ||
2054 | file.$ngfDimensionPromise = deferred.promise; |
|
2055 | file.$ngfDimensionPromise['finally'](function () { |
|
2056 | delete file.$ngfDimensionPromise; |
|
2057 | }); |
|
2058 | return file.$ngfDimensionPromise; |
|
2059 | }; |
|
2060 | ||
2061 | upload.mediaDuration = function (file) { |
|
2062 | if (file.$ngfDuration) { |
|
2063 | var d = $q.defer(); |
|
2064 | $timeout(function () { |
|
2065 | d.resolve(file.$ngfDuration); |
|
2066 | }); |
|
2067 | return d.promise; |
|
2068 | } |
|
2069 | if (file.$ngfDurationPromise) return file.$ngfDurationPromise; |
|
2070 | ||
2071 | var deferred = $q.defer(); |
|
2072 | $timeout(function () { |
|
2073 | if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) { |
|
2074 | deferred.reject('not media'); |
|
2075 | return; |
|
2076 | } |
|
2077 | upload.dataUrl(file).then(function (dataUrl) { |
|
2078 | var el = angular.element(file.type.indexOf('audio') === 0 ? '<audio>' : '<video>') |
|
2079 | .attr('src', dataUrl).css('visibility', 'none').css('position', 'fixed'); |
|
2080 | ||
2081 | function success() { |
|
2082 | var duration = el[0].duration; |
|
2083 | file.$ngfDuration = duration; |
|
2084 | el.remove(); |
|
2085 | deferred.resolve(duration); |
|
2086 | } |
|
2087 | ||
2088 | function error() { |
|
2089 | el.remove(); |
|
2090 | deferred.reject('load error'); |
|
2091 | } |
|
2092 | ||
2093 | el.on('loadedmetadata', success); |
|
2094 | el.on('error', error); |
|
2095 | var count = 0; |
|
2096 | ||
2097 | function checkLoadError() { |
|
2098 | $timeout(function () { |
|
2099 | if (el[0].parentNode) { |
|
2100 | if (el[0].duration) { |
|
2101 | success(); |
|
2102 | } else if (count > 10) { |
|
2103 | error(); |
|
2104 | } else { |
|
2105 | checkLoadError(); |
|
2106 | } |
|
2107 | } |
|
2108 | }, 1000); |
|
2109 | } |
|
2110 | ||
2111 | checkLoadError(); |
|
2112 | ||
2113 | angular.element(document.body).append(el); |
|
2114 | }, function () { |
|
2115 | deferred.reject('load error'); |
|
2116 | }); |
|
2117 | }); |
|
2118 | ||
2119 | file.$ngfDurationPromise = deferred.promise; |
|
2120 | file.$ngfDurationPromise['finally'](function () { |
|
2121 | delete file.$ngfDurationPromise; |
|
2122 | }); |
|
2123 | return file.$ngfDurationPromise; |
|
2124 | }; |
|
2125 | return upload; |
|
2126 | } |
|
2127 | ]); |
|
2128 | ||
2129 | ngFileUpload.service('UploadResize', ['UploadValidate', '$q', function (UploadValidate, $q) { |
@@ 1204-1704 (lines=501) @@ | ||
1201 | ||
1202 | })(); |
|
1203 | ||
1204 | ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) { |
|
1205 | var upload = UploadDataUrl; |
|
1206 | ||
1207 | function globStringToRegex(str) { |
|
1208 | var regexp = '', excludes = []; |
|
1209 | if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { |
|
1210 | regexp = str.substring(1, str.length - 1); |
|
1211 | } else { |
|
1212 | var split = str.split(','); |
|
1213 | if (split.length > 1) { |
|
1214 | for (var i = 0; i < split.length; i++) { |
|
1215 | var r = globStringToRegex(split[i]); |
|
1216 | if (r.regexp) { |
|
1217 | regexp += '(' + r.regexp + ')'; |
|
1218 | if (i < split.length - 1) { |
|
1219 | regexp += '|'; |
|
1220 | } |
|
1221 | } else { |
|
1222 | excludes = excludes.concat(r.excludes); |
|
1223 | } |
|
1224 | } |
|
1225 | } else { |
|
1226 | if (str.indexOf('!') === 0) { |
|
1227 | excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$'); |
|
1228 | } else { |
|
1229 | if (str.indexOf('.') === 0) { |
|
1230 | str = '*' + str; |
|
1231 | } |
|
1232 | regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$'; |
|
1233 | regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); |
|
1234 | } |
|
1235 | } |
|
1236 | } |
|
1237 | return {regexp: regexp, excludes: excludes}; |
|
1238 | } |
|
1239 | ||
1240 | upload.validatePattern = function (file, val) { |
|
1241 | if (!val) { |
|
1242 | return true; |
|
1243 | } |
|
1244 | var pattern = globStringToRegex(val), valid = true; |
|
1245 | if (pattern.regexp && pattern.regexp.length) { |
|
1246 | var regexp = new RegExp(pattern.regexp, 'i'); |
|
1247 | valid = (file.type != null && regexp.test(file.type)) || |
|
1248 | (file.name != null && regexp.test(file.name)); |
|
1249 | } |
|
1250 | var len = pattern.excludes.length; |
|
1251 | while (len--) { |
|
1252 | var exclude = new RegExp(pattern.excludes[len], 'i'); |
|
1253 | valid = valid && (file.type == null || exclude.test(file.type)) && |
|
1254 | (file.name == null || exclude.test(file.name)); |
|
1255 | } |
|
1256 | return valid; |
|
1257 | }; |
|
1258 | ||
1259 | upload.ratioToFloat = function (val) { |
|
1260 | var r = val.toString(), xIndex = r.search(/[x:]/i); |
|
1261 | if (xIndex > -1) { |
|
1262 | r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1)); |
|
1263 | } else { |
|
1264 | r = parseFloat(r); |
|
1265 | } |
|
1266 | return r; |
|
1267 | }; |
|
1268 | ||
1269 | upload.registerModelChangeValidator = function (ngModel, attr, scope) { |
|
1270 | if (ngModel) { |
|
1271 | ngModel.$formatters.push(function (files) { |
|
1272 | if (ngModel.$dirty) { |
|
1273 | var filesArray = files; |
|
1274 | if (files && !angular.isArray(files)) { |
|
1275 | filesArray = [files]; |
|
1276 | } |
|
1277 | upload.validate(filesArray, 0, ngModel, attr, scope).then(function () { |
|
1278 | upload.applyModelValidation(ngModel, filesArray); |
|
1279 | }); |
|
1280 | } |
|
1281 | return files; |
|
1282 | }); |
|
1283 | } |
|
1284 | }; |
|
1285 | ||
1286 | function markModelAsDirty(ngModel, files) { |
|
1287 | if (files != null && !ngModel.$dirty) { |
|
1288 | if (ngModel.$setDirty) { |
|
1289 | ngModel.$setDirty(); |
|
1290 | } else { |
|
1291 | ngModel.$dirty = true; |
|
1292 | } |
|
1293 | } |
|
1294 | } |
|
1295 | ||
1296 | upload.applyModelValidation = function (ngModel, files) { |
|
1297 | markModelAsDirty(ngModel, files); |
|
1298 | angular.forEach(ngModel.$ngfValidations, function (validation) { |
|
1299 | ngModel.$setValidity(validation.name, validation.valid); |
|
1300 | }); |
|
1301 | }; |
|
1302 | ||
1303 | upload.getValidationAttr = function (attr, scope, name, validationName, file) { |
|
1304 | var dName = 'ngf' + name[0].toUpperCase() + name.substr(1); |
|
1305 | var val = upload.attrGetter(dName, attr, scope, {$file: file}); |
|
1306 | if (val == null) { |
|
1307 | val = upload.attrGetter('ngfValidate', attr, scope, {$file: file}); |
|
1308 | if (val) { |
|
1309 | var split = (validationName || name).split('.'); |
|
1310 | val = val[split[0]]; |
|
1311 | if (split.length > 1) { |
|
1312 | val = val && val[split[1]]; |
|
1313 | } |
|
1314 | } |
|
1315 | } |
|
1316 | return val; |
|
1317 | }; |
|
1318 | ||
1319 | upload.validate = function (files, prevLength, ngModel, attr, scope) { |
|
1320 | ngModel = ngModel || {}; |
|
1321 | ngModel.$ngfValidations = ngModel.$ngfValidations || []; |
|
1322 | ||
1323 | angular.forEach(ngModel.$ngfValidations, function (v) { |
|
1324 | v.valid = true; |
|
1325 | }); |
|
1326 | ||
1327 | var attrGetter = function (name, params) { |
|
1328 | return upload.attrGetter(name, attr, scope, params); |
|
1329 | }; |
|
1330 | ||
1331 | var ignoredErrors = (upload.attrGetter('ngfIgnoreInvalid', attr, scope) || '').split(' '); |
|
1332 | var runAllValidation = upload.attrGetter('ngfRunAllValidations', attr, scope); |
|
1333 | ||
1334 | if (files == null || files.length === 0) { |
|
1335 | return upload.emptyPromise({'validFiles': files, 'invalidFiles': []}); |
|
1336 | } |
|
1337 | ||
1338 | files = files.length === undefined ? [files] : files.slice(0); |
|
1339 | var invalidFiles = []; |
|
1340 | ||
1341 | function validateSync(name, validationName, fn) { |
|
1342 | if (files) { |
|
1343 | var i = files.length, valid = null; |
|
1344 | while (i--) { |
|
1345 | var file = files[i]; |
|
1346 | if (file) { |
|
1347 | var val = upload.getValidationAttr(attr, scope, name, validationName, file); |
|
1348 | if (val != null) { |
|
1349 | if (!fn(file, val, i)) { |
|
1350 | if (ignoredErrors.indexOf(name) === -1) { |
|
1351 | file.$error = name; |
|
1352 | (file.$errorMessages = (file.$errorMessages || {}))[name] = true; |
|
1353 | file.$errorParam = val; |
|
1354 | if (invalidFiles.indexOf(file) === -1) { |
|
1355 | invalidFiles.push(file); |
|
1356 | } |
|
1357 | if (!runAllValidation) { |
|
1358 | files.splice(i, 1); |
|
1359 | } |
|
1360 | valid = false; |
|
1361 | } else { |
|
1362 | files.splice(i, 1); |
|
1363 | } |
|
1364 | } |
|
1365 | } |
|
1366 | } |
|
1367 | } |
|
1368 | if (valid !== null) { |
|
1369 | ngModel.$ngfValidations.push({name: name, valid: valid}); |
|
1370 | } |
|
1371 | } |
|
1372 | } |
|
1373 | ||
1374 | validateSync('pattern', null, upload.validatePattern); |
|
1375 | validateSync('minSize', 'size.min', function (file, val) { |
|
1376 | return file.size + 0.1 >= upload.translateScalars(val); |
|
1377 | }); |
|
1378 | validateSync('maxSize', 'size.max', function (file, val) { |
|
1379 | return file.size - 0.1 <= upload.translateScalars(val); |
|
1380 | }); |
|
1381 | var totalSize = 0; |
|
1382 | validateSync('maxTotalSize', null, function (file, val) { |
|
1383 | totalSize += file.size; |
|
1384 | if (totalSize > upload.translateScalars(val)) { |
|
1385 | files.splice(0, files.length); |
|
1386 | return false; |
|
1387 | } |
|
1388 | return true; |
|
1389 | }); |
|
1390 | ||
1391 | validateSync('validateFn', null, function (file, r) { |
|
1392 | return r === true || r === null || r === ''; |
|
1393 | }); |
|
1394 | ||
1395 | if (!files.length) { |
|
1396 | return upload.emptyPromise({'validFiles': [], 'invalidFiles': invalidFiles}); |
|
1397 | } |
|
1398 | ||
1399 | function validateAsync(name, validationName, type, asyncFn, fn) { |
|
1400 | function resolveResult(defer, file, val) { |
|
1401 | function resolveInternal(fn) { |
|
1402 | if (fn()) { |
|
1403 | if (ignoredErrors.indexOf(name) === -1) { |
|
1404 | file.$error = name; |
|
1405 | (file.$errorMessages = (file.$errorMessages || {}))[name] = true; |
|
1406 | file.$errorParam = val; |
|
1407 | if (invalidFiles.indexOf(file) === -1) { |
|
1408 | invalidFiles.push(file); |
|
1409 | } |
|
1410 | if (!runAllValidation) { |
|
1411 | var i = files.indexOf(file); |
|
1412 | if (i > -1) files.splice(i, 1); |
|
1413 | } |
|
1414 | defer.resolve(false); |
|
1415 | } else { |
|
1416 | var j = files.indexOf(file); |
|
1417 | if (j > -1) files.splice(j, 1); |
|
1418 | defer.resolve(true); |
|
1419 | } |
|
1420 | } else { |
|
1421 | defer.resolve(true); |
|
1422 | } |
|
1423 | } |
|
1424 | ||
1425 | if (val != null) { |
|
1426 | asyncFn(file, val).then(function (d) { |
|
1427 | resolveInternal(function () { |
|
1428 | return !fn(d, val); |
|
1429 | }); |
|
1430 | }, function () { |
|
1431 | resolveInternal(function () { |
|
1432 | return attrGetter('ngfValidateForce', {$file: file}); |
|
1433 | }); |
|
1434 | }); |
|
1435 | } else { |
|
1436 | defer.resolve(true); |
|
1437 | } |
|
1438 | } |
|
1439 | ||
1440 | var promises = [upload.emptyPromise(true)]; |
|
1441 | if (files) { |
|
1442 | files = files.length === undefined ? [files] : files; |
|
1443 | angular.forEach(files, function (file) { |
|
1444 | var defer = $q.defer(); |
|
1445 | promises.push(defer.promise); |
|
1446 | if (type && (file.type == null || file.type.search(type) !== 0)) { |
|
1447 | defer.resolve(true); |
|
1448 | return; |
|
1449 | } |
|
1450 | if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) { |
|
1451 | upload.imageDimensions(file).then(function (d) { |
|
1452 | resolveResult(defer, file, |
|
1453 | attrGetter('ngfDimensions', {$file: file, $width: d.width, $height: d.height})); |
|
1454 | }, function () { |
|
1455 | defer.resolve(false); |
|
1456 | }); |
|
1457 | } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) { |
|
1458 | upload.mediaDuration(file).then(function (d) { |
|
1459 | resolveResult(defer, file, |
|
1460 | attrGetter('ngfDuration', {$file: file, $duration: d})); |
|
1461 | }, function () { |
|
1462 | defer.resolve(false); |
|
1463 | }); |
|
1464 | } else { |
|
1465 | resolveResult(defer, file, |
|
1466 | upload.getValidationAttr(attr, scope, name, validationName, file)); |
|
1467 | } |
|
1468 | }); |
|
1469 | } |
|
1470 | var deffer = $q.defer(); |
|
1471 | $q.all(promises).then(function (values) { |
|
1472 | var isValid = true; |
|
1473 | for (var i = 0; i < values.length; i++) { |
|
1474 | if (!values[i]) { |
|
1475 | isValid = false; |
|
1476 | break; |
|
1477 | } |
|
1478 | } |
|
1479 | ngModel.$ngfValidations.push({name: name, valid: isValid}); |
|
1480 | deffer.resolve(isValid); |
|
1481 | }); |
|
1482 | return deffer.promise; |
|
1483 | } |
|
1484 | ||
1485 | var deffer = $q.defer(); |
|
1486 | var promises = []; |
|
1487 | ||
1488 | promises.push(validateAsync('maxHeight', 'height.max', /image/, |
|
1489 | this.imageDimensions, function (d, val) { |
|
1490 | return d.height <= val; |
|
1491 | })); |
|
1492 | promises.push(validateAsync('minHeight', 'height.min', /image/, |
|
1493 | this.imageDimensions, function (d, val) { |
|
1494 | return d.height >= val; |
|
1495 | })); |
|
1496 | promises.push(validateAsync('maxWidth', 'width.max', /image/, |
|
1497 | this.imageDimensions, function (d, val) { |
|
1498 | return d.width <= val; |
|
1499 | })); |
|
1500 | promises.push(validateAsync('minWidth', 'width.min', /image/, |
|
1501 | this.imageDimensions, function (d, val) { |
|
1502 | return d.width >= val; |
|
1503 | })); |
|
1504 | promises.push(validateAsync('dimensions', null, /image/, |
|
1505 | function (file, val) { |
|
1506 | return upload.emptyPromise(val); |
|
1507 | }, function (r) { |
|
1508 | return r; |
|
1509 | })); |
|
1510 | promises.push(validateAsync('ratio', null, /image/, |
|
1511 | this.imageDimensions, function (d, val) { |
|
1512 | var split = val.toString().split(','), valid = false; |
|
1513 | for (var i = 0; i < split.length; i++) { |
|
1514 | if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.01) { |
|
1515 | valid = true; |
|
1516 | } |
|
1517 | } |
|
1518 | return valid; |
|
1519 | })); |
|
1520 | promises.push(validateAsync('maxRatio', 'ratio.max', /image/, |
|
1521 | this.imageDimensions, function (d, val) { |
|
1522 | return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001; |
|
1523 | })); |
|
1524 | promises.push(validateAsync('minRatio', 'ratio.min', /image/, |
|
1525 | this.imageDimensions, function (d, val) { |
|
1526 | return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001; |
|
1527 | })); |
|
1528 | promises.push(validateAsync('maxDuration', 'duration.max', /audio|video/, |
|
1529 | this.mediaDuration, function (d, val) { |
|
1530 | return d <= upload.translateScalars(val); |
|
1531 | })); |
|
1532 | promises.push(validateAsync('minDuration', 'duration.min', /audio|video/, |
|
1533 | this.mediaDuration, function (d, val) { |
|
1534 | return d >= upload.translateScalars(val); |
|
1535 | })); |
|
1536 | promises.push(validateAsync('duration', null, /audio|video/, |
|
1537 | function (file, val) { |
|
1538 | return upload.emptyPromise(val); |
|
1539 | }, function (r) { |
|
1540 | return r; |
|
1541 | })); |
|
1542 | ||
1543 | promises.push(validateAsync('validateAsyncFn', null, null, |
|
1544 | function (file, val) { |
|
1545 | return val; |
|
1546 | }, function (r) { |
|
1547 | return r === true || r === null || r === ''; |
|
1548 | })); |
|
1549 | ||
1550 | $q.all(promises).then(function () { |
|
1551 | ||
1552 | if (runAllValidation) { |
|
1553 | for (var i = 0; i < files.length; i++) { |
|
1554 | var file = files[i]; |
|
1555 | if (file.$error) { |
|
1556 | files.splice(i--, 1); |
|
1557 | } |
|
1558 | } |
|
1559 | } |
|
1560 | ||
1561 | runAllValidation = false; |
|
1562 | validateSync('maxFiles', null, function (file, val, i) { |
|
1563 | return prevLength + i < val; |
|
1564 | }); |
|
1565 | ||
1566 | deffer.resolve({'validFiles': files, 'invalidFiles': invalidFiles}); |
|
1567 | }); |
|
1568 | return deffer.promise; |
|
1569 | }; |
|
1570 | ||
1571 | upload.imageDimensions = function (file) { |
|
1572 | if (file.$ngfWidth && file.$ngfHeight) { |
|
1573 | var d = $q.defer(); |
|
1574 | $timeout(function () { |
|
1575 | d.resolve({width: file.$ngfWidth, height: file.$ngfHeight}); |
|
1576 | }); |
|
1577 | return d.promise; |
|
1578 | } |
|
1579 | if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise; |
|
1580 | ||
1581 | var deferred = $q.defer(); |
|
1582 | $timeout(function () { |
|
1583 | if (file.type.indexOf('image') !== 0) { |
|
1584 | deferred.reject('not image'); |
|
1585 | return; |
|
1586 | } |
|
1587 | upload.dataUrl(file).then(function (dataUrl) { |
|
1588 | var img = angular.element('<img>').attr('src', dataUrl) |
|
1589 | .css('visibility', 'hidden').css('position', 'fixed') |
|
1590 | .css('max-width', 'none !important').css('max-height', 'none !important'); |
|
1591 | ||
1592 | function success() { |
|
1593 | var width = img[0].naturalWidth || img[0].clientWidth; |
|
1594 | var height = img[0].naturalHeight || img[0].clientHeight; |
|
1595 | img.remove(); |
|
1596 | file.$ngfWidth = width; |
|
1597 | file.$ngfHeight = height; |
|
1598 | deferred.resolve({width: width, height: height}); |
|
1599 | } |
|
1600 | ||
1601 | function error() { |
|
1602 | img.remove(); |
|
1603 | deferred.reject('load error'); |
|
1604 | } |
|
1605 | ||
1606 | img.on('load', success); |
|
1607 | img.on('error', error); |
|
1608 | ||
1609 | var secondsCounter = 0; |
|
1610 | function checkLoadErrorInCaseOfNoCallback() { |
|
1611 | $timeout(function () { |
|
1612 | if (img[0].parentNode) { |
|
1613 | if (img[0].clientWidth) { |
|
1614 | success(); |
|
1615 | } else if (secondsCounter++ > 10) { |
|
1616 | error(); |
|
1617 | } else { |
|
1618 | checkLoadErrorInCaseOfNoCallback(); |
|
1619 | } |
|
1620 | } |
|
1621 | }, 1000); |
|
1622 | } |
|
1623 | ||
1624 | checkLoadErrorInCaseOfNoCallback(); |
|
1625 | ||
1626 | angular.element(document.getElementsByTagName('body')[0]).append(img); |
|
1627 | }, function () { |
|
1628 | deferred.reject('load error'); |
|
1629 | }); |
|
1630 | }); |
|
1631 | ||
1632 | file.$ngfDimensionPromise = deferred.promise; |
|
1633 | file.$ngfDimensionPromise['finally'](function () { |
|
1634 | delete file.$ngfDimensionPromise; |
|
1635 | }); |
|
1636 | return file.$ngfDimensionPromise; |
|
1637 | }; |
|
1638 | ||
1639 | upload.mediaDuration = function (file) { |
|
1640 | if (file.$ngfDuration) { |
|
1641 | var d = $q.defer(); |
|
1642 | $timeout(function () { |
|
1643 | d.resolve(file.$ngfDuration); |
|
1644 | }); |
|
1645 | return d.promise; |
|
1646 | } |
|
1647 | if (file.$ngfDurationPromise) return file.$ngfDurationPromise; |
|
1648 | ||
1649 | var deferred = $q.defer(); |
|
1650 | $timeout(function () { |
|
1651 | if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) { |
|
1652 | deferred.reject('not media'); |
|
1653 | return; |
|
1654 | } |
|
1655 | upload.dataUrl(file).then(function (dataUrl) { |
|
1656 | var el = angular.element(file.type.indexOf('audio') === 0 ? '<audio>' : '<video>') |
|
1657 | .attr('src', dataUrl).css('visibility', 'none').css('position', 'fixed'); |
|
1658 | ||
1659 | function success() { |
|
1660 | var duration = el[0].duration; |
|
1661 | file.$ngfDuration = duration; |
|
1662 | el.remove(); |
|
1663 | deferred.resolve(duration); |
|
1664 | } |
|
1665 | ||
1666 | function error() { |
|
1667 | el.remove(); |
|
1668 | deferred.reject('load error'); |
|
1669 | } |
|
1670 | ||
1671 | el.on('loadedmetadata', success); |
|
1672 | el.on('error', error); |
|
1673 | var count = 0; |
|
1674 | ||
1675 | function checkLoadError() { |
|
1676 | $timeout(function () { |
|
1677 | if (el[0].parentNode) { |
|
1678 | if (el[0].duration) { |
|
1679 | success(); |
|
1680 | } else if (count > 10) { |
|
1681 | error(); |
|
1682 | } else { |
|
1683 | checkLoadError(); |
|
1684 | } |
|
1685 | } |
|
1686 | }, 1000); |
|
1687 | } |
|
1688 | ||
1689 | checkLoadError(); |
|
1690 | ||
1691 | angular.element(document.body).append(el); |
|
1692 | }, function () { |
|
1693 | deferred.reject('load error'); |
|
1694 | }); |
|
1695 | }); |
|
1696 | ||
1697 | file.$ngfDurationPromise = deferred.promise; |
|
1698 | file.$ngfDurationPromise['finally'](function () { |
|
1699 | delete file.$ngfDurationPromise; |
|
1700 | }); |
|
1701 | return file.$ngfDurationPromise; |
|
1702 | }; |
|
1703 | return upload; |
|
1704 | } |
|
1705 | ]); |
|
1706 | ||
1707 | ngFileUpload.service('UploadResize', ['UploadValidate', '$q', function (UploadValidate, $q) { |
@@ 1-501 (lines=501) @@ | ||
1 | ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) { |
|
2 | var upload = UploadDataUrl; |
|
3 | ||
4 | function globStringToRegex(str) { |
|
5 | var regexp = '', excludes = []; |
|
6 | if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { |
|
7 | regexp = str.substring(1, str.length - 1); |
|
8 | } else { |
|
9 | var split = str.split(','); |
|
10 | if (split.length > 1) { |
|
11 | for (var i = 0; i < split.length; i++) { |
|
12 | var r = globStringToRegex(split[i]); |
|
13 | if (r.regexp) { |
|
14 | regexp += '(' + r.regexp + ')'; |
|
15 | if (i < split.length - 1) { |
|
16 | regexp += '|'; |
|
17 | } |
|
18 | } else { |
|
19 | excludes = excludes.concat(r.excludes); |
|
20 | } |
|
21 | } |
|
22 | } else { |
|
23 | if (str.indexOf('!') === 0) { |
|
24 | excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$'); |
|
25 | } else { |
|
26 | if (str.indexOf('.') === 0) { |
|
27 | str = '*' + str; |
|
28 | } |
|
29 | regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$'; |
|
30 | regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); |
|
31 | } |
|
32 | } |
|
33 | } |
|
34 | return {regexp: regexp, excludes: excludes}; |
|
35 | } |
|
36 | ||
37 | upload.validatePattern = function (file, val) { |
|
38 | if (!val) { |
|
39 | return true; |
|
40 | } |
|
41 | var pattern = globStringToRegex(val), valid = true; |
|
42 | if (pattern.regexp && pattern.regexp.length) { |
|
43 | var regexp = new RegExp(pattern.regexp, 'i'); |
|
44 | valid = (file.type != null && regexp.test(file.type)) || |
|
45 | (file.name != null && regexp.test(file.name)); |
|
46 | } |
|
47 | var len = pattern.excludes.length; |
|
48 | while (len--) { |
|
49 | var exclude = new RegExp(pattern.excludes[len], 'i'); |
|
50 | valid = valid && (file.type == null || exclude.test(file.type)) && |
|
51 | (file.name == null || exclude.test(file.name)); |
|
52 | } |
|
53 | return valid; |
|
54 | }; |
|
55 | ||
56 | upload.ratioToFloat = function (val) { |
|
57 | var r = val.toString(), xIndex = r.search(/[x:]/i); |
|
58 | if (xIndex > -1) { |
|
59 | r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1)); |
|
60 | } else { |
|
61 | r = parseFloat(r); |
|
62 | } |
|
63 | return r; |
|
64 | }; |
|
65 | ||
66 | upload.registerModelChangeValidator = function (ngModel, attr, scope) { |
|
67 | if (ngModel) { |
|
68 | ngModel.$formatters.push(function (files) { |
|
69 | if (ngModel.$dirty) { |
|
70 | var filesArray = files; |
|
71 | if (files && !angular.isArray(files)) { |
|
72 | filesArray = [files]; |
|
73 | } |
|
74 | upload.validate(filesArray, 0, ngModel, attr, scope).then(function () { |
|
75 | upload.applyModelValidation(ngModel, filesArray); |
|
76 | }); |
|
77 | } |
|
78 | return files; |
|
79 | }); |
|
80 | } |
|
81 | }; |
|
82 | ||
83 | function markModelAsDirty(ngModel, files) { |
|
84 | if (files != null && !ngModel.$dirty) { |
|
85 | if (ngModel.$setDirty) { |
|
86 | ngModel.$setDirty(); |
|
87 | } else { |
|
88 | ngModel.$dirty = true; |
|
89 | } |
|
90 | } |
|
91 | } |
|
92 | ||
93 | upload.applyModelValidation = function (ngModel, files) { |
|
94 | markModelAsDirty(ngModel, files); |
|
95 | angular.forEach(ngModel.$ngfValidations, function (validation) { |
|
96 | ngModel.$setValidity(validation.name, validation.valid); |
|
97 | }); |
|
98 | }; |
|
99 | ||
100 | upload.getValidationAttr = function (attr, scope, name, validationName, file) { |
|
101 | var dName = 'ngf' + name[0].toUpperCase() + name.substr(1); |
|
102 | var val = upload.attrGetter(dName, attr, scope, {$file: file}); |
|
103 | if (val == null) { |
|
104 | val = upload.attrGetter('ngfValidate', attr, scope, {$file: file}); |
|
105 | if (val) { |
|
106 | var split = (validationName || name).split('.'); |
|
107 | val = val[split[0]]; |
|
108 | if (split.length > 1) { |
|
109 | val = val && val[split[1]]; |
|
110 | } |
|
111 | } |
|
112 | } |
|
113 | return val; |
|
114 | }; |
|
115 | ||
116 | upload.validate = function (files, prevLength, ngModel, attr, scope) { |
|
117 | ngModel = ngModel || {}; |
|
118 | ngModel.$ngfValidations = ngModel.$ngfValidations || []; |
|
119 | ||
120 | angular.forEach(ngModel.$ngfValidations, function (v) { |
|
121 | v.valid = true; |
|
122 | }); |
|
123 | ||
124 | var attrGetter = function (name, params) { |
|
125 | return upload.attrGetter(name, attr, scope, params); |
|
126 | }; |
|
127 | ||
128 | var ignoredErrors = (upload.attrGetter('ngfIgnoreInvalid', attr, scope) || '').split(' '); |
|
129 | var runAllValidation = upload.attrGetter('ngfRunAllValidations', attr, scope); |
|
130 | ||
131 | if (files == null || files.length === 0) { |
|
132 | return upload.emptyPromise({'validFiles': files, 'invalidFiles': []}); |
|
133 | } |
|
134 | ||
135 | files = files.length === undefined ? [files] : files.slice(0); |
|
136 | var invalidFiles = []; |
|
137 | ||
138 | function validateSync(name, validationName, fn) { |
|
139 | if (files) { |
|
140 | var i = files.length, valid = null; |
|
141 | while (i--) { |
|
142 | var file = files[i]; |
|
143 | if (file) { |
|
144 | var val = upload.getValidationAttr(attr, scope, name, validationName, file); |
|
145 | if (val != null) { |
|
146 | if (!fn(file, val, i)) { |
|
147 | if (ignoredErrors.indexOf(name) === -1) { |
|
148 | file.$error = name; |
|
149 | (file.$errorMessages = (file.$errorMessages || {}))[name] = true; |
|
150 | file.$errorParam = val; |
|
151 | if (invalidFiles.indexOf(file) === -1) { |
|
152 | invalidFiles.push(file); |
|
153 | } |
|
154 | if (!runAllValidation) { |
|
155 | files.splice(i, 1); |
|
156 | } |
|
157 | valid = false; |
|
158 | } else { |
|
159 | files.splice(i, 1); |
|
160 | } |
|
161 | } |
|
162 | } |
|
163 | } |
|
164 | } |
|
165 | if (valid !== null) { |
|
166 | ngModel.$ngfValidations.push({name: name, valid: valid}); |
|
167 | } |
|
168 | } |
|
169 | } |
|
170 | ||
171 | validateSync('pattern', null, upload.validatePattern); |
|
172 | validateSync('minSize', 'size.min', function (file, val) { |
|
173 | return file.size + 0.1 >= upload.translateScalars(val); |
|
174 | }); |
|
175 | validateSync('maxSize', 'size.max', function (file, val) { |
|
176 | return file.size - 0.1 <= upload.translateScalars(val); |
|
177 | }); |
|
178 | var totalSize = 0; |
|
179 | validateSync('maxTotalSize', null, function (file, val) { |
|
180 | totalSize += file.size; |
|
181 | if (totalSize > upload.translateScalars(val)) { |
|
182 | files.splice(0, files.length); |
|
183 | return false; |
|
184 | } |
|
185 | return true; |
|
186 | }); |
|
187 | ||
188 | validateSync('validateFn', null, function (file, r) { |
|
189 | return r === true || r === null || r === ''; |
|
190 | }); |
|
191 | ||
192 | if (!files.length) { |
|
193 | return upload.emptyPromise({'validFiles': [], 'invalidFiles': invalidFiles}); |
|
194 | } |
|
195 | ||
196 | function validateAsync(name, validationName, type, asyncFn, fn) { |
|
197 | function resolveResult(defer, file, val) { |
|
198 | function resolveInternal(fn) { |
|
199 | if (fn()) { |
|
200 | if (ignoredErrors.indexOf(name) === -1) { |
|
201 | file.$error = name; |
|
202 | (file.$errorMessages = (file.$errorMessages || {}))[name] = true; |
|
203 | file.$errorParam = val; |
|
204 | if (invalidFiles.indexOf(file) === -1) { |
|
205 | invalidFiles.push(file); |
|
206 | } |
|
207 | if (!runAllValidation) { |
|
208 | var i = files.indexOf(file); |
|
209 | if (i > -1) files.splice(i, 1); |
|
210 | } |
|
211 | defer.resolve(false); |
|
212 | } else { |
|
213 | var j = files.indexOf(file); |
|
214 | if (j > -1) files.splice(j, 1); |
|
215 | defer.resolve(true); |
|
216 | } |
|
217 | } else { |
|
218 | defer.resolve(true); |
|
219 | } |
|
220 | } |
|
221 | ||
222 | if (val != null) { |
|
223 | asyncFn(file, val).then(function (d) { |
|
224 | resolveInternal(function () { |
|
225 | return !fn(d, val); |
|
226 | }); |
|
227 | }, function () { |
|
228 | resolveInternal(function () { |
|
229 | return attrGetter('ngfValidateForce', {$file: file}); |
|
230 | }); |
|
231 | }); |
|
232 | } else { |
|
233 | defer.resolve(true); |
|
234 | } |
|
235 | } |
|
236 | ||
237 | var promises = [upload.emptyPromise(true)]; |
|
238 | if (files) { |
|
239 | files = files.length === undefined ? [files] : files; |
|
240 | angular.forEach(files, function (file) { |
|
241 | var defer = $q.defer(); |
|
242 | promises.push(defer.promise); |
|
243 | if (type && (file.type == null || file.type.search(type) !== 0)) { |
|
244 | defer.resolve(true); |
|
245 | return; |
|
246 | } |
|
247 | if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) { |
|
248 | upload.imageDimensions(file).then(function (d) { |
|
249 | resolveResult(defer, file, |
|
250 | attrGetter('ngfDimensions', {$file: file, $width: d.width, $height: d.height})); |
|
251 | }, function () { |
|
252 | defer.resolve(false); |
|
253 | }); |
|
254 | } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) { |
|
255 | upload.mediaDuration(file).then(function (d) { |
|
256 | resolveResult(defer, file, |
|
257 | attrGetter('ngfDuration', {$file: file, $duration: d})); |
|
258 | }, function () { |
|
259 | defer.resolve(false); |
|
260 | }); |
|
261 | } else { |
|
262 | resolveResult(defer, file, |
|
263 | upload.getValidationAttr(attr, scope, name, validationName, file)); |
|
264 | } |
|
265 | }); |
|
266 | } |
|
267 | var deffer = $q.defer(); |
|
268 | $q.all(promises).then(function (values) { |
|
269 | var isValid = true; |
|
270 | for (var i = 0; i < values.length; i++) { |
|
271 | if (!values[i]) { |
|
272 | isValid = false; |
|
273 | break; |
|
274 | } |
|
275 | } |
|
276 | ngModel.$ngfValidations.push({name: name, valid: isValid}); |
|
277 | deffer.resolve(isValid); |
|
278 | }); |
|
279 | return deffer.promise; |
|
280 | } |
|
281 | ||
282 | var deffer = $q.defer(); |
|
283 | var promises = []; |
|
284 | ||
285 | promises.push(validateAsync('maxHeight', 'height.max', /image/, |
|
286 | this.imageDimensions, function (d, val) { |
|
287 | return d.height <= val; |
|
288 | })); |
|
289 | promises.push(validateAsync('minHeight', 'height.min', /image/, |
|
290 | this.imageDimensions, function (d, val) { |
|
291 | return d.height >= val; |
|
292 | })); |
|
293 | promises.push(validateAsync('maxWidth', 'width.max', /image/, |
|
294 | this.imageDimensions, function (d, val) { |
|
295 | return d.width <= val; |
|
296 | })); |
|
297 | promises.push(validateAsync('minWidth', 'width.min', /image/, |
|
298 | this.imageDimensions, function (d, val) { |
|
299 | return d.width >= val; |
|
300 | })); |
|
301 | promises.push(validateAsync('dimensions', null, /image/, |
|
302 | function (file, val) { |
|
303 | return upload.emptyPromise(val); |
|
304 | }, function (r) { |
|
305 | return r; |
|
306 | })); |
|
307 | promises.push(validateAsync('ratio', null, /image/, |
|
308 | this.imageDimensions, function (d, val) { |
|
309 | var split = val.toString().split(','), valid = false; |
|
310 | for (var i = 0; i < split.length; i++) { |
|
311 | if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.01) { |
|
312 | valid = true; |
|
313 | } |
|
314 | } |
|
315 | return valid; |
|
316 | })); |
|
317 | promises.push(validateAsync('maxRatio', 'ratio.max', /image/, |
|
318 | this.imageDimensions, function (d, val) { |
|
319 | return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001; |
|
320 | })); |
|
321 | promises.push(validateAsync('minRatio', 'ratio.min', /image/, |
|
322 | this.imageDimensions, function (d, val) { |
|
323 | return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001; |
|
324 | })); |
|
325 | promises.push(validateAsync('maxDuration', 'duration.max', /audio|video/, |
|
326 | this.mediaDuration, function (d, val) { |
|
327 | return d <= upload.translateScalars(val); |
|
328 | })); |
|
329 | promises.push(validateAsync('minDuration', 'duration.min', /audio|video/, |
|
330 | this.mediaDuration, function (d, val) { |
|
331 | return d >= upload.translateScalars(val); |
|
332 | })); |
|
333 | promises.push(validateAsync('duration', null, /audio|video/, |
|
334 | function (file, val) { |
|
335 | return upload.emptyPromise(val); |
|
336 | }, function (r) { |
|
337 | return r; |
|
338 | })); |
|
339 | ||
340 | promises.push(validateAsync('validateAsyncFn', null, null, |
|
341 | function (file, val) { |
|
342 | return val; |
|
343 | }, function (r) { |
|
344 | return r === true || r === null || r === ''; |
|
345 | })); |
|
346 | ||
347 | $q.all(promises).then(function () { |
|
348 | ||
349 | if (runAllValidation) { |
|
350 | for (var i = 0; i < files.length; i++) { |
|
351 | var file = files[i]; |
|
352 | if (file.$error) { |
|
353 | files.splice(i--, 1); |
|
354 | } |
|
355 | } |
|
356 | } |
|
357 | ||
358 | runAllValidation = false; |
|
359 | validateSync('maxFiles', null, function (file, val, i) { |
|
360 | return prevLength + i < val; |
|
361 | }); |
|
362 | ||
363 | deffer.resolve({'validFiles': files, 'invalidFiles': invalidFiles}); |
|
364 | }); |
|
365 | return deffer.promise; |
|
366 | }; |
|
367 | ||
368 | upload.imageDimensions = function (file) { |
|
369 | if (file.$ngfWidth && file.$ngfHeight) { |
|
370 | var d = $q.defer(); |
|
371 | $timeout(function () { |
|
372 | d.resolve({width: file.$ngfWidth, height: file.$ngfHeight}); |
|
373 | }); |
|
374 | return d.promise; |
|
375 | } |
|
376 | if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise; |
|
377 | ||
378 | var deferred = $q.defer(); |
|
379 | $timeout(function () { |
|
380 | if (file.type.indexOf('image') !== 0) { |
|
381 | deferred.reject('not image'); |
|
382 | return; |
|
383 | } |
|
384 | upload.dataUrl(file).then(function (dataUrl) { |
|
385 | var img = angular.element('<img>').attr('src', dataUrl) |
|
386 | .css('visibility', 'hidden').css('position', 'fixed') |
|
387 | .css('max-width', 'none !important').css('max-height', 'none !important'); |
|
388 | ||
389 | function success() { |
|
390 | var width = img[0].naturalWidth || img[0].clientWidth; |
|
391 | var height = img[0].naturalHeight || img[0].clientHeight; |
|
392 | img.remove(); |
|
393 | file.$ngfWidth = width; |
|
394 | file.$ngfHeight = height; |
|
395 | deferred.resolve({width: width, height: height}); |
|
396 | } |
|
397 | ||
398 | function error() { |
|
399 | img.remove(); |
|
400 | deferred.reject('load error'); |
|
401 | } |
|
402 | ||
403 | img.on('load', success); |
|
404 | img.on('error', error); |
|
405 | ||
406 | var secondsCounter = 0; |
|
407 | function checkLoadErrorInCaseOfNoCallback() { |
|
408 | $timeout(function () { |
|
409 | if (img[0].parentNode) { |
|
410 | if (img[0].clientWidth) { |
|
411 | success(); |
|
412 | } else if (secondsCounter++ > 10) { |
|
413 | error(); |
|
414 | } else { |
|
415 | checkLoadErrorInCaseOfNoCallback(); |
|
416 | } |
|
417 | } |
|
418 | }, 1000); |
|
419 | } |
|
420 | ||
421 | checkLoadErrorInCaseOfNoCallback(); |
|
422 | ||
423 | angular.element(document.getElementsByTagName('body')[0]).append(img); |
|
424 | }, function () { |
|
425 | deferred.reject('load error'); |
|
426 | }); |
|
427 | }); |
|
428 | ||
429 | file.$ngfDimensionPromise = deferred.promise; |
|
430 | file.$ngfDimensionPromise['finally'](function () { |
|
431 | delete file.$ngfDimensionPromise; |
|
432 | }); |
|
433 | return file.$ngfDimensionPromise; |
|
434 | }; |
|
435 | ||
436 | upload.mediaDuration = function (file) { |
|
437 | if (file.$ngfDuration) { |
|
438 | var d = $q.defer(); |
|
439 | $timeout(function () { |
|
440 | d.resolve(file.$ngfDuration); |
|
441 | }); |
|
442 | return d.promise; |
|
443 | } |
|
444 | if (file.$ngfDurationPromise) return file.$ngfDurationPromise; |
|
445 | ||
446 | var deferred = $q.defer(); |
|
447 | $timeout(function () { |
|
448 | if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) { |
|
449 | deferred.reject('not media'); |
|
450 | return; |
|
451 | } |
|
452 | upload.dataUrl(file).then(function (dataUrl) { |
|
453 | var el = angular.element(file.type.indexOf('audio') === 0 ? '<audio>' : '<video>') |
|
454 | .attr('src', dataUrl).css('visibility', 'none').css('position', 'fixed'); |
|
455 | ||
456 | function success() { |
|
457 | var duration = el[0].duration; |
|
458 | file.$ngfDuration = duration; |
|
459 | el.remove(); |
|
460 | deferred.resolve(duration); |
|
461 | } |
|
462 | ||
463 | function error() { |
|
464 | el.remove(); |
|
465 | deferred.reject('load error'); |
|
466 | } |
|
467 | ||
468 | el.on('loadedmetadata', success); |
|
469 | el.on('error', error); |
|
470 | var count = 0; |
|
471 | ||
472 | function checkLoadError() { |
|
473 | $timeout(function () { |
|
474 | if (el[0].parentNode) { |
|
475 | if (el[0].duration) { |
|
476 | success(); |
|
477 | } else if (count > 10) { |
|
478 | error(); |
|
479 | } else { |
|
480 | checkLoadError(); |
|
481 | } |
|
482 | } |
|
483 | }, 1000); |
|
484 | } |
|
485 | ||
486 | checkLoadError(); |
|
487 | ||
488 | angular.element(document.body).append(el); |
|
489 | }, function () { |
|
490 | deferred.reject('load error'); |
|
491 | }); |
|
492 | }); |
|
493 | ||
494 | file.$ngfDurationPromise = deferred.promise; |
|
495 | file.$ngfDurationPromise['finally'](function () { |
|
496 | delete file.$ngfDurationPromise; |
|
497 | }); |
|
498 | return file.$ngfDurationPromise; |
|
499 | }; |
|
500 | return upload; |
|
501 | } |
|
502 | ]); |
|
503 |