├── .babelrc ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── fileup.bootstrap.min.css ├── fileup.bootstrap.min.css.map ├── fileup.css ├── fileup.js ├── fileup.min.css ├── fileup.min.css.map ├── fileup.min.js └── fileup.min.js.map ├── gulpfile.js ├── package.json └── src ├── css ├── _varaible.scss ├── fileup.bootstrap.scss ├── fileup.scss └── main.scss ├── html └── file.html └── js ├── fileup.events.js ├── fileup.file.js ├── fileup.instance.js ├── fileup.js ├── fileup.private.js ├── fileup.templates.js ├── fileup.utils.js ├── lang ├── en.js ├── es.js ├── pt.js └── ru.js └── main.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": {} 6 | }, 7 | "rules": { 8 | "constructor-super": 2, 9 | "for-direction": 2, 10 | "getter-return": 2, 11 | "no-async-promise-executor": 2, 12 | "no-case-declarations": 2, 13 | "no-class-assign": 2, 14 | "no-compare-neg-zero": 2, 15 | "no-const-assign": 2, 16 | "no-debugger": 2, 17 | "no-dupe-args": 2, 18 | "no-dupe-class-members": 2, 19 | "no-dupe-else-if": 2, 20 | "no-dupe-keys": 2, 21 | "no-duplicate-case": 2, 22 | "no-empty-character-class": 2, 23 | "no-empty-pattern": 2, 24 | "no-ex-assign": 2, 25 | "no-import-assign": 2, 26 | "no-invalid-regexp": 2, 27 | "no-irregular-whitespace": 2, 28 | "no-misleading-character-class": 2, 29 | "no-new-symbol": 2, 30 | "no-obj-calls": 2, 31 | "no-octal": 2, 32 | "no-regex-spaces": 2, 33 | "no-self-assign": 2, 34 | "no-setter-return": 2, 35 | "no-sparse-arrays": 2, 36 | "no-this-before-super": 2, 37 | "no-unexpected-multiline": 2, 38 | "no-unreachable": 2, 39 | "no-unsafe-finally": 2, 40 | "no-unsafe-negation": 2, 41 | "no-unused-labels": 2, 42 | "no-useless-catch": 2, 43 | "require-yield": 2, 44 | "use-isnan": 2, 45 | "valid-typeof": 2 46 | }, 47 | "env": { 48 | "browser": true, 49 | "jquery": true 50 | } 51 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FileUp 2 | Библиотека для загрузки файлов на сервер. 3 | Поддерживает выбор нескольких файлов, перетаскивание и индикатор выполнения. 4 | 5 | [[DEMO]](https://n2ref.github.io/fileup/) 6 | 7 | ![Interface](https://raw.githubusercontent.com/n2ref/fileup/gh-pages/src/img/preview.png) 8 | 9 | ## Documentation 10 | 11 | ### Install with npm 12 | ```shell 13 | $ npm install fileup-js 14 | ``` 15 | 16 | ### 1. Events 17 | 18 | - onSelect (file) 19 | - onRemove (file) 20 | - onBeforeStart (file, xhr) 21 | - onStart (file) 22 | - onProgress (file, ProgressEvent) 23 | - onSuccess (file, response) 24 | - onError (errorType, options) 25 | - onAbort (file) 26 | - onFinish (file) 27 | - onDragEnter (event) 28 | - onDragOver (event) 29 | - onDragLeave (event) 30 | - onDragEnd (event) 31 | 32 | ### 2. Options 33 | 34 | - id: '', 35 | - url: '', 36 | - input: '', 37 | - queue: '', 38 | - dropzone: '', 39 | - files: [], 40 | - fieldName: 'file', 41 | - extraFields: {}, 42 | - lang: 'en', 43 | - sizeLimit: 0, 44 | - filesLimit: 0, 45 | - httpMethod: 'post', 46 | - timeout: null, 47 | - autostart: false, 48 | - templateFile: 49 | 50 | ```html 51 |
52 |
53 | [NAME] 54 | 55 |
56 |
57 |
58 | [NAME] 59 | ([SIZE]) 60 |
61 | 62 |
63 | 64 | [UPLOAD] 65 | 66 |
67 | 68 |
69 | 70 |
71 |
72 |
73 |
74 |
75 | ``` 76 | -------------------------------------------------------------------------------- /dist/fileup.css: -------------------------------------------------------------------------------- 1 | .fileup-file { 2 | font-size: 12px; 3 | width: 350px; 4 | position: relative; 5 | overflow: hidden; 6 | } 7 | .fileup-file.fileup-image .fileup-preview.no-preview { 8 | width: 80px; 9 | height: 56px; 10 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA4CAAAAABP3SBGAAACW0lEQVRYw+3Y3U8aQRAAcP7/98bb3YFDxSvUBCKlxigllVrFILFEmmJrxdYrB7e7Q+JfUD4Ocgcs7OX2oQ/OAwmZ8Mt+zO7NkcraZiOVSRuNTMp+BU2BzCIJAyIgK19eJIv6PoRB0njBRDHiBRYG6Rf0EwXvv/3fQM6Ngsjd5/GHKZCLdvkgV2wOhSmwRglj1Kp43AgoG9a05mCnKk2AvO+w4HjZj8IAKLsAwcmlLdQEl4oiCt6x+VVAGpogf3KFEhQPmWCEwNpSAZKLKHh0NNwwxhIJZuy4XAvEO2adqGctuzad7gm9RV8H5H6RpklVqMWOM74Arb2m9LVAbFFIA6lJteg2q6dXT9LXArlXmMwIyLla5CglCl8PxMZszWGlOKX+9RUC+XMuqDOg1xGx31Ev6wYQz6153QK0wkt7Qz4rRTUoe7uLg5Bm6a+jxU9ch9LToYwL4pkVesyyTAdDIwer/FfGA+XP+bmaXyjfZxn5y54krMMexgJFhURbAbp7P02JD7MEyXUxBojfAJaaC7r/gNPTGCSo3cYYIyzRlXaFOj3JB4eLBIPGasUrQLylsNoAkfzvl6vQVjH6iQstkA/e0XUtFSneH7DQdyDHntQB8Zqsb9JYdmngVukPbge5GxlHOGB1GR5wKyjrRL+zpHsd3AJyL09j9KoscxPqRdaDBRan+2WsLrhJcLzZZ0NhEpzcFa7cBDo7NGa8cX5w9S57teOTuFH5OHs0ry9sIeOHP9TpHJL32MlBLx8FL0c8UYhB9D0FCpX3yaKchei7Hk0a8PoCbgw0/a/IP7jBCOMW5IDRAAAAAElFTkSuQmCC); 11 | background-repeat: no-repeat; 12 | background-size: contain; 13 | } 14 | .fileup-file.fileup-image .fileup-preview.no-preview img { 15 | display: none; 16 | } 17 | .fileup-file.fileup-image .fileup-preview .fileup-icon { 18 | display: none; 19 | } 20 | .fileup-file.fileup-doc .fileup-preview img { 21 | display: none; 22 | } 23 | .fileup-file .fileup-preview img { 24 | max-height: 80px; 25 | max-width: 80px; 26 | } 27 | .fileup-file .fileup-description { 28 | padding-right: 20px; 29 | word-break: break-all; 30 | } 31 | .fileup-file .fileup-controls .fileup-remove { 32 | cursor: pointer; 33 | position: absolute; 34 | top: 0; 35 | right: 2px; 36 | width: 16px; 37 | height: 16px; 38 | font-size: 16px; 39 | color: #9e9e9e; 40 | } 41 | .fileup-file .fileup-controls .fileup-remove:hover { 42 | color: #616161; 43 | } 44 | .fileup-file .fileup-controls .fileup-upload, 45 | .fileup-file .fileup-controls .fileup-abort { 46 | cursor: pointer; 47 | } 48 | .fileup-file .fileup-result.fileup-success { 49 | color: #4CAE4C; 50 | } 51 | .fileup-file .fileup-result.fileup-error { 52 | color: #CE0000; 53 | } 54 | .fileup-file .fileup-progress { 55 | height: 3px; 56 | } 57 | .fileup-file .fileup-progress-bar { 58 | width: 1px; 59 | -webkit-transition: width 0.1s ease-out 0.1s; 60 | -moz-transition: width 0.1s ease-out 0.1s; 61 | -o-transition: width 0.1s ease-out 0.1s; 62 | transition: width 0.1s ease-out 0.1s; 63 | } 64 | 65 | .fileup-btn { 66 | position: relative; 67 | overflow: hidden; 68 | display: inline-block; 69 | } 70 | .fileup-btn input { 71 | position: absolute; 72 | top: 0; 73 | right: 0; 74 | margin: 0; 75 | opacity: 0; 76 | -ms-filter: "alpha(opacity=0)"; 77 | font-size: 200px; 78 | direction: ltr; 79 | cursor: pointer; 80 | } 81 | 82 | .fileup-dropzone { 83 | background-color: #f6f6ff; 84 | border-width: 1px; 85 | border-style: dashed; 86 | border-color: #0018ff; 87 | cursor: pointer; 88 | } 89 | .fileup-dropzone.over { 90 | opacity: 0.7; 91 | border-style: solid; 92 | } -------------------------------------------------------------------------------- /dist/fileup.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.fileUp = factory()); 5 | })(this, (function () { 'use strict'; 6 | 7 | function _typeof(o) { 8 | "@babel/helpers - typeof"; 9 | 10 | return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { 11 | return typeof o; 12 | } : function (o) { 13 | return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; 14 | }, _typeof(o); 15 | } 16 | 17 | var fileUpUtils = { 18 | /** 19 | * Проверка на объект 20 | * @param value 21 | */ 22 | isObject: function isObject(value) { 23 | return _typeof(value) === 'object' && !Array.isArray(value) && value !== null; 24 | }, 25 | /** 26 | * Проверка на число 27 | * @param num 28 | * @returns {boolean} 29 | * @private 30 | */ 31 | isNumeric: function isNumeric(num) { 32 | return (typeof num === 'number' || typeof num === "string" && num.trim() !== '') && !isNaN(num); 33 | }, 34 | /** 35 | * Получение размера файла в байтах 36 | * @param {File} file 37 | * @return {int|null} 38 | */ 39 | getFileSize: function getFileSize(file) { 40 | if (!(file instanceof File)) { 41 | return null; 42 | } 43 | return file.size || file.fileSize; 44 | }, 45 | /** 46 | * Получение названия файла 47 | * @param {File} file 48 | * @return {string|null} 49 | */ 50 | getFileName: function getFileName(file) { 51 | if (!(file instanceof File)) { 52 | return null; 53 | } 54 | return file.name || file.fileName; 55 | }, 56 | /** 57 | * Formatting size 58 | * @param {int} size 59 | * @returns {string} 60 | */ 61 | getSizeHuman: function getSizeHuman(size) { 62 | if (!fileUpUtils.isNumeric(size)) { 63 | return ''; 64 | } 65 | size = Number(size); 66 | var result = ''; 67 | if (size >= 1073741824) { 68 | result = (size / 1073741824).toFixed(2) + ' Gb'; 69 | } else if (size >= 1048576) { 70 | result = (size / 1048576).toFixed(2) + ' Mb'; 71 | } else if (size >= 1024) { 72 | result = (size / 1024).toFixed(2) + ' Kb'; 73 | } else if (size >= 0) { 74 | result = size + ' bytes'; 75 | } 76 | return result; 77 | }, 78 | /** 79 | * Создание уникальной строки хэша 80 | * @returns {string} 81 | * @private 82 | */ 83 | hashCode: function hashCode() { 84 | return this.crc32((new Date().getTime() + Math.random()).toString()).toString(16); 85 | }, 86 | /** 87 | * Hash crc32 88 | * @param str 89 | * @returns {number} 90 | * @private 91 | */ 92 | crc32: function crc32(str) { 93 | for (var a, o = [], c = 0; c < 256; c++) { 94 | a = c; 95 | for (var f = 0; f < 8; f++) { 96 | a = 1 & a ? 3988292384 ^ a >>> 1 : a >>> 1; 97 | } 98 | o[c] = a; 99 | } 100 | for (var n = -1, t = 0; t < str.length; t++) { 101 | n = n >>> 8 ^ o[255 & (n ^ str.charCodeAt(t))]; 102 | } 103 | return (-1 ^ n) >>> 0; 104 | } 105 | }; 106 | 107 | var fileUpEvents = { 108 | /** 109 | * Событие начала загрузки 110 | * @param {object} file 111 | */ 112 | onLoadStart: function onLoadStart(file) { 113 | var $file = file.getElement(); 114 | if ($file) { 115 | $file.find('.fileup-upload').hide(); 116 | $file.find('.fileup-abort').show(); 117 | $file.find('.fileup-result').removeClass('fileup-error').removeClass('fileup-success').text(''); 118 | } 119 | }, 120 | /** 121 | * Событие начала изменения прогресса загрузки 122 | * @param {object} file 123 | * @param {ProgressEvent} ProgressEvent 124 | */ 125 | onLoadProgress: function onLoadProgress(file, ProgressEvent) { 126 | if (ProgressEvent.lengthComputable) { 127 | var percent = Math.ceil(ProgressEvent.loaded / ProgressEvent.total * 100); 128 | var $file = file.getElement(); 129 | if ($file) { 130 | $file.find('.fileup-progress-bar').css('width', percent + "%"); 131 | } 132 | } 133 | }, 134 | /** 135 | * Событие начала загрузки 136 | * @param {object} file 137 | */ 138 | onLoadAbort: function onLoadAbort(file) { 139 | var $file = file.getElement(); 140 | if ($file) { 141 | $file.find('.fileup-abort').hide(); 142 | $file.find('.fileup-upload').show(); 143 | $file.find('.fileup-result').removeClass('fileup-error').removeClass('fileup-success').text(''); 144 | } 145 | }, 146 | /** 147 | * Событие успешной загрузки файла 148 | * @param {object} file 149 | */ 150 | onSuccess: function onSuccess(file) { 151 | var $file = file.getElement(); 152 | if ($file) { 153 | var lang = this.getLang(); 154 | $file.find('.fileup-abort').hide(); 155 | $file.find('.fileup-upload').hide(); 156 | $file.find('.fileup-result').removeClass('fileup-error').addClass('fileup-success').text(lang.complete); 157 | } 158 | }, 159 | /** 160 | * Событие ошибки 161 | * @param {string} eventName 162 | * @param {object} options 163 | */ 164 | onError: function onError(eventName, options) { 165 | var lang = this.getLang(); 166 | switch (eventName) { 167 | case 'files_limit': 168 | alert(lang.errorFilesLimit.replace(/%filesLimit%/g, options.filesLimit)); 169 | break; 170 | case 'size_limit': 171 | var size = fileUpUtils.getSizeHuman(options.sizeLimit); 172 | var message = lang.errorSizeLimit; 173 | message = message.replace(/%sizeLimit%/g, size); 174 | message = message.replace(/%fileName%/g, fileUpUtils.getFileName(options.fileData)); 175 | alert(message); 176 | break; 177 | case 'file_type': 178 | alert(lang.errorFileType.replace(/%fileName%/g, fileUpUtils.getFileName(options.fileData))); 179 | break; 180 | case 'load_bad_status': 181 | case 'load_error': 182 | case 'load_timeout': 183 | var $file = options.file.getElement(); 184 | if ($file) { 185 | var _message = eventName === 'load_bad_status' ? lang.errorBadStatus : lang.errorLoad; 186 | $file.find('.fileup-abort').hide(); 187 | $file.find('.fileup-upload').show(); 188 | $file.find('.fileup-result').addClass('fileup-error').text(_message); 189 | } 190 | break; 191 | case 'old_browser': 192 | alert(lang.errorOldBrowser); 193 | break; 194 | } 195 | }, 196 | /** 197 | * Событие переноса файла через dropzone 198 | * @param {Event} event 199 | */ 200 | onDragOver: function onDragOver(event) { 201 | event.stopPropagation(); 202 | event.preventDefault(); 203 | event.dataTransfer.dropEffect = 'copy'; 204 | var dropzone = this.getDropzone(); 205 | if (dropzone) { 206 | dropzone.addClass('over'); 207 | } 208 | }, 209 | /** 210 | * Событие завершения перетаскивания с отпускаем кнопки мыши 211 | * @param {Event} event 212 | */ 213 | onDragLeave: function onDragLeave(event) { 214 | var dropzone = this.getDropzone(); 215 | if (dropzone) { 216 | dropzone.removeClass('over'); 217 | } 218 | }, 219 | /** 220 | * Событие когда перетаскиваемый элемент или выделенный текст покидают допустимую цель перетаскивания 221 | * @param {Event} event 222 | */ 223 | onDragEnd: function onDragEnd(event) { 224 | var dropzone = this.getDropzone(); 225 | if (dropzone) { 226 | dropzone.removeClass('over'); 227 | } 228 | }, 229 | /** 230 | * Событие переноса файла в dropzone 231 | * @param {Event} event 232 | */ 233 | onDragEnter: function onDragEnter(event) { 234 | event.stopPropagation(); 235 | event.preventDefault(); 236 | event.dataTransfer.dropEffect = 'copy'; 237 | } 238 | }; 239 | 240 | var fileUpPrivate = { 241 | /** 242 | * 243 | * @param {object} fileUp 244 | */ 245 | initInput: function initInput(fileUp) { 246 | var input = null; 247 | if (fileUp._options.input instanceof HTMLElement || fileUp._options.input instanceof jQuery) { 248 | input = $(fileUp._options.input); 249 | } else if (typeof fileUp._options.input === 'string' && fileUp._options.input) { 250 | input = $('#' + fileUp._options.input); 251 | } 252 | if (!input || !$(input)[0] || $(input)[0].type !== 'file') { 253 | throw new Error('Not found input element'); 254 | } 255 | fileUp._input = input; 256 | }, 257 | /** 258 | * 259 | * @param {object} fileUp 260 | */ 261 | initQueue: function initQueue(fileUp) { 262 | var queue = null; 263 | if (fileUp._options.queue instanceof HTMLElement || fileUp._options.queue instanceof jQuery) { 264 | queue = $(fileUp._options.queue); 265 | } else if (typeof fileUp._options.queue === 'string' && fileUp._options.queue) { 266 | queue = $('#' + fileUp._options.queue); 267 | } 268 | if (!queue || !$(queue)[0]) { 269 | throw new Error('Not found queue element'); 270 | } 271 | fileUp._queue = queue; 272 | }, 273 | /** 274 | * 275 | * @param {object} fileUp 276 | */ 277 | initDropzone: function initDropzone(fileUp) { 278 | var dropzone = null; 279 | if (fileUp._options.dropzone instanceof HTMLElement || fileUp._options.dropzone instanceof jQuery) { 280 | dropzone = $(fileUp._options.dropzone); 281 | } else if (typeof fileUp._options.dropzone === 'string' && fileUp._options.dropzone) { 282 | dropzone = $('#' + fileUp._options.dropzone); 283 | } 284 | if (dropzone) { 285 | fileUp._dropzone = dropzone; 286 | var that = this; 287 | dropzone.on('click', function () { 288 | fileUp.getInput().click(); 289 | }); 290 | dropzone[0].addEventListener('dragover', function (event) { 291 | that.trigger(fileUp, 'drag_over', [event]); 292 | }); 293 | dropzone[0].addEventListener('dragleave', function (event) { 294 | that.trigger(fileUp, 'drag_leave', [event]); 295 | }); 296 | dropzone[0].addEventListener('dragenter', function (event) { 297 | that.trigger(fileUp, 'drag_enter', [event]); 298 | }); 299 | dropzone[0].addEventListener('dragend', function (event) { 300 | that.trigger(fileUp, 'drag_end', [event]); 301 | }); 302 | dropzone[0].addEventListener('drop', function (event) { 303 | fileUp.getInput()[0].files = event.target.files || event.dataTransfer.files; 304 | that.appendFiles(fileUp, event); 305 | }); 306 | } 307 | }, 308 | /** 309 | * Инициализация событий 310 | * @param {object} fileUp 311 | */ 312 | initEvents: function initEvents(fileUp) { 313 | /** 314 | * @param {string} name 315 | * @param {function|string} func 316 | */ 317 | function setEvent(name, func) { 318 | var event = null; 319 | if (typeof func === 'function') { 320 | event = func; 321 | } else if (typeof func === 'string') { 322 | event = new Function(func); 323 | } 324 | if (event) { 325 | fileUp.on(name, event); 326 | } 327 | } 328 | var options = fileUp.getOptions(); 329 | var that = this; 330 | setEvent('load_start', fileUpEvents.onLoadStart); 331 | setEvent('load_progress', fileUpEvents.onLoadProgress); 332 | setEvent('load_abort', fileUpEvents.onLoadAbort); 333 | setEvent('load_success', fileUpEvents.onSuccess); 334 | setEvent('error', fileUpEvents.onError); 335 | setEvent('drag_over', fileUpEvents.onDragOver); 336 | setEvent('drag_leave', fileUpEvents.onDragEnter); 337 | setEvent('drag_end', fileUpEvents.onDragLeave); 338 | setEvent('drag_enter', fileUpEvents.onDragEnd); 339 | if (options.onSelect) { 340 | setEvent('select', options.onSelect); 341 | } 342 | if (options.onRemove) { 343 | setEvent('remove', options.onRemove); 344 | } 345 | if (options.onBeforeStart) { 346 | setEvent('load_before_start', options.onBeforeStart); 347 | } 348 | if (options.onStart) { 349 | setEvent('load_start', options.onStart); 350 | } 351 | if (options.onProgress) { 352 | setEvent('load_progress', options.onProgress); 353 | } 354 | if (options.onAbort) { 355 | setEvent('load_abort', options.onAbort); 356 | } 357 | if (options.onSuccess) { 358 | setEvent('load_success', options.onSuccess); 359 | } 360 | if (options.onFinish) { 361 | setEvent('load_finish', options.onFinish); 362 | } 363 | if (options.onError) { 364 | setEvent('error', options.onError); 365 | } 366 | if (options.onDragOver) { 367 | setEvent('drag_over', options.onDragOver); 368 | } 369 | if (options.onDragLeave) { 370 | setEvent('drag_leave', options.onDragLeave); 371 | } 372 | if (options.onDragEnd) { 373 | setEvent('drag_end', options.onDragEnd); 374 | } 375 | if (options.onDragEnter) { 376 | setEvent('drag_enter', options.onDragEnter); 377 | } 378 | fileUp.getInput().on('change', function (event) { 379 | that.appendFiles(fileUp, event); 380 | }); 381 | }, 382 | /** 383 | * Формирование списка ранее загруженных файлов 384 | * @param {object} fileUp 385 | */ 386 | renderFiles: function renderFiles(fileUp) { 387 | var options = fileUp.getOptions(); 388 | if (Array.isArray(options.files) && options.files.length > 0) { 389 | for (var i = 0; i < options.files.length; i++) { 390 | if (!fileUpUtils.isObject(options.files[i])) { 391 | continue; 392 | } 393 | fileUp.appendFileByData(options.files[i]); 394 | } 395 | } 396 | }, 397 | /** 398 | * @param fileUp 399 | * @param name 400 | * @param params 401 | * @return {object} 402 | * @private 403 | */ 404 | trigger: function trigger(fileUp, name, params) { 405 | params = params || []; 406 | var results = []; 407 | if (fileUp._events[name] instanceof Object && fileUp._events[name].length > 0) { 408 | for (var i = 0; i < fileUp._events[name].length; i++) { 409 | var callback = fileUp._events[name][i].callback; 410 | results.push(callback.apply(fileUp._events[name][i].context || fileUp, params)); 411 | if (fileUp._events[name][i].singleExec) { 412 | fileUp._events[name].splice(i, 1); 413 | i--; 414 | } 415 | } 416 | } 417 | return results; 418 | }, 419 | /** 420 | * Append files in queue 421 | * @param {object} fileUp 422 | * @param {Event} event 423 | */ 424 | appendFiles: function appendFiles(fileUp, event) { 425 | var _this = this; 426 | event.preventDefault(); 427 | event.stopPropagation(); 428 | var options = fileUp.getOptions(); 429 | var input = fileUp.getInput(); 430 | var files = input[0].files; 431 | var multiple = input.is("[multiple]"); 432 | if (files.length > 0) { 433 | var _loop = function _loop() { 434 | var file = files[i]; 435 | if (options.sizeLimit > 0 && fileUpUtils.getFileSize(file) > options.sizeLimit) { 436 | _this.trigger(fileUp, 'error', ['size_limit', { 437 | fileData: file, 438 | sizeLimit: options.sizeLimit 439 | }]); 440 | return 0; // continue 441 | } 442 | if (options.filesLimit > 0 && Object.keys(fileUp._files).length >= options.filesLimit) { 443 | _this.trigger(fileUp, 'error', ['files_limit', { 444 | fileData: file, 445 | filesLimit: options.filesLimit 446 | }]); 447 | return 1; // break 448 | } 449 | if (typeof input[0].accept === 'string') { 450 | var accept = input[0].accept; 451 | if (accept && /[^\w]+/.test(accept)) { 452 | var isAccept = false; 453 | var types = accept.split(','); 454 | if (types.length > 0) { 455 | for (t = 0; t < types.length; t++) { 456 | types[t] = types[t].replace(/\s/g, ''); 457 | if (new RegExp(types[t].replace('*', '.*')).test(file.type) || new RegExp(types[t].replace('.', '.*/')).test(file.type)) { 458 | isAccept = true; 459 | break; 460 | } 461 | } 462 | } 463 | if (!isAccept) { 464 | _this.trigger(fileUp, 'error', ['file_type', { 465 | fileData: file 466 | }]); 467 | return 0; // continue 468 | } 469 | } 470 | } 471 | var results = _this.trigger(fileUp, 'select', [file]); 472 | if (results) { 473 | var isContinue = false; 474 | $.each(results, function (key, result) { 475 | if (result === false) { 476 | isContinue = true; 477 | return false; 478 | } 479 | }); 480 | if (isContinue) { 481 | return 0; // continue 482 | } 483 | } 484 | if (!multiple) { 485 | fileUp.removeAll(); 486 | } 487 | fileUp.appendFile(file); 488 | if (!multiple) { 489 | return 1; // break 490 | } 491 | }, 492 | t, 493 | _ret; 494 | for (var i = 0; i < files.length; i++) { 495 | _ret = _loop(); 496 | if (_ret === 0) continue; 497 | if (_ret === 1) break; 498 | } 499 | input.val(''); 500 | } 501 | this.trigger(fileUp, 'dragEnd', [event]); 502 | } 503 | }; 504 | 505 | var fileUpFile = { 506 | _options: { 507 | name: null, 508 | size: null, 509 | urlPreview: null, 510 | urlDownload: null 511 | }, 512 | _id: '', 513 | _status: 'stand_by', 514 | _fileElement: null, 515 | _file: null, 516 | _fileUp: null, 517 | _xhr: null, 518 | /** 519 | * Инициализация 520 | * @param {object} fileUp 521 | * @param {int} id 522 | * @param {object} options 523 | * @param {File} file 524 | * @private 525 | */ 526 | _init: function _init(fileUp, id, options, file) { 527 | if (!fileUpUtils.isObject(options)) { 528 | throw new Error('File incorrect options param'); 529 | } 530 | if (typeof id !== 'number' || id < 0) { 531 | throw new Error('File dont set or incorrect id param'); 532 | } 533 | if (typeof options.name !== 'string' || !options.name) { 534 | throw new Error('File dont set name param'); 535 | } 536 | this._fileUp = fileUp; 537 | this._options = $.extend(true, {}, this._options, options); 538 | this._id = id; 539 | if (file instanceof File) { 540 | var xhr = null; 541 | if (window.XMLHttpRequest) { 542 | xhr = "onload" in new XMLHttpRequest() ? new XMLHttpRequest() : new XDomainRequest(); 543 | } else if (window.ActiveXObject) { 544 | try { 545 | xhr = new ActiveXObject("Msxml2.XMLHTTP"); 546 | } catch (e) { 547 | try { 548 | xhr = new ActiveXObject("Microsoft.XMLHTTP"); 549 | } catch (e) { 550 | fileUpPrivate.trigger(fileUp, 'error', ['old_browser', { 551 | file: this 552 | }]); 553 | } 554 | } 555 | } else { 556 | fileUpPrivate.trigger(fileUp, 'error', ['old_browser', { 557 | file: this 558 | }]); 559 | } 560 | if (!xhr) { 561 | throw new Error('xhr dont created. Check your browser'); 562 | } 563 | this._xhr = xhr; 564 | this._file = file; 565 | } else { 566 | this._status = 'finish'; 567 | } 568 | }, 569 | /** 570 | * Получение id файла 571 | * @return {null} 572 | */ 573 | getId: function getId() { 574 | return this._id; 575 | }, 576 | /** 577 | * Получение name 578 | * @return {string|null} 579 | */ 580 | getName: function getName() { 581 | return this._file ? fileUpUtils.getFileName(this._file) : this._options.name; 582 | }, 583 | /** 584 | * Получение элемента файла 585 | * @return {jQuery|null} 586 | */ 587 | getElement: function getElement() { 588 | return this._fileElement; 589 | }, 590 | /** 591 | * Получение urlPreview 592 | * @return {string|null} 593 | */ 594 | getUrlPreview: function getUrlPreview() { 595 | return this._options.urlPreview; 596 | }, 597 | /** 598 | * Получение urlDownload 599 | * @return {string|null} 600 | */ 601 | getUrlDownload: function getUrlDownload() { 602 | return this._options.urlDownload; 603 | }, 604 | /** 605 | * Получение size 606 | * @return {int|null} 607 | */ 608 | getSize: function getSize() { 609 | return this._file ? fileUpUtils.getFileSize(this._file) : this._options.size; 610 | }, 611 | /** 612 | * Formatting size 613 | * @returns {string} 614 | */ 615 | getSizeHuman: function getSizeHuman() { 616 | var size = this.getSize(); 617 | return fileUpUtils.getSizeHuman(size); 618 | }, 619 | /** 620 | * Получение xhr 621 | * @return {XMLHttpRequest|null} 622 | */ 623 | getXhr: function getXhr() { 624 | return this._xhr; 625 | }, 626 | /** 627 | * Получение файла 628 | * @return {File|null} 629 | */ 630 | getFile: function getFile() { 631 | if (!(this._file instanceof File)) { 632 | return null; 633 | } 634 | return this._file; 635 | }, 636 | /** 637 | * Получение статуса 638 | * @return {string} 639 | */ 640 | getStatus: function getStatus() { 641 | return this._status; 642 | }, 643 | /** 644 | * Установка статуса 645 | * @param {string} status 646 | */ 647 | setStatus: function setStatus(status) { 648 | if (typeof status !== 'string') { 649 | return; 650 | } 651 | this._status = status; 652 | }, 653 | /** 654 | * Получение параметров 655 | * 656 | * @returns {object} 657 | */ 658 | getOptions: function getOptions() { 659 | return this._options; 660 | }, 661 | /** 662 | * Получение параметра 663 | * @param {string} name 664 | * @returns {*} 665 | */ 666 | getOption: function getOption(name) { 667 | if (typeof name !== 'string' || !this._options.hasOwnProperty(name)) { 668 | return null; 669 | } 670 | return this._options[name]; 671 | }, 672 | /** 673 | * Установка параметра 674 | * @param {string} name 675 | * @param {*} value 676 | */ 677 | setOption: function setOption(name, value) { 678 | if (typeof name !== 'string') { 679 | return; 680 | } 681 | this._options[name] = value; 682 | }, 683 | /** 684 | * Показ сообщения об ошибке 685 | * @param {string} message 686 | */ 687 | showError: function showError(message) { 688 | if (typeof message !== 'string') { 689 | return; 690 | } 691 | var element = this.getElement(); 692 | if (element) { 693 | element.find('.fileup-result').removeClass('fileup-success').addClass('fileup-error').text(message); 694 | } 695 | }, 696 | /** 697 | * Показ сообщения об успехе 698 | * @param {string} message 699 | */ 700 | showSuccess: function showSuccess(message) { 701 | if (typeof message !== 'string') { 702 | return; 703 | } 704 | var element = this.getElement(); 705 | if (element) { 706 | element.find('.fileup-result').removeClass('fileup-error').addClass('fileup-success').text(message); 707 | } 708 | }, 709 | /** 710 | * Удаление файла на странице и из памяти 711 | */ 712 | remove: function remove() { 713 | this.abort(); 714 | if (this._fileElement) { 715 | this._fileElement.fadeOut('fast', function () { 716 | this.remove(); 717 | }); 718 | } 719 | var fileId = this.getId(); 720 | if (this._fileUp._files.hasOwnProperty(fileId)) { 721 | delete this._fileUp._files[fileId]; 722 | } 723 | fileUpPrivate.trigger(this._fileUp, 'remove', [this]); 724 | }, 725 | /** 726 | * Загрузка файла 727 | * @return {boolean} 728 | */ 729 | upload: function upload() { 730 | var file = this.getFile(); 731 | var xhr = this.getXhr(); 732 | if (!file || !xhr) { 733 | return false; 734 | } 735 | var options = this._fileUp.getOptions(); 736 | var that = this; 737 | if (typeof options.timeout === 'number') { 738 | xhr.timeout = options.timeout; 739 | } 740 | 741 | // запрос начат 742 | xhr.onloadstart = function () { 743 | that.setStatus('load_start'); 744 | fileUpPrivate.trigger(that._fileUp, 'load_start', [that]); 745 | }; 746 | 747 | // браузер получил очередной пакет данных 748 | xhr.upload.onprogress = function (ProgressEvent) { 749 | fileUpPrivate.trigger(that._fileUp, 'load_progress', [that, ProgressEvent]); 750 | }; 751 | 752 | // запрос был успешно (без ошибок) завершён 753 | xhr.onload = function () { 754 | that.setStatus('loaded'); 755 | if (xhr.status === 200) { 756 | fileUpPrivate.trigger(that._fileUp, 'load_success', [that, xhr.responseText]); 757 | } else { 758 | fileUpPrivate.trigger(that._fileUp, 'error', ['load_bad_status', { 759 | file: that, 760 | fileData: file, 761 | response: xhr.responseText, 762 | xhr: xhr 763 | }]); 764 | } 765 | }; 766 | 767 | // запрос был завершён (успешно или неуспешно) 768 | xhr.onloadend = function () { 769 | that.setStatus('finish'); 770 | fileUpPrivate.trigger(that._fileUp, 'load_finish', [that]); 771 | }; 772 | 773 | // запрос был отменён вызовом xhr.abort() 774 | xhr.onabort = function () { 775 | that.setStatus('stand_by'); 776 | fileUpPrivate.trigger(that._fileUp, 'load_abort', [that]); 777 | }; 778 | 779 | // запрос был прекращён по таймауту 780 | xhr.ontimeout = function () { 781 | that.setStatus('stand_by'); 782 | fileUpPrivate.trigger(that._fileUp, 'error', ['load_timeout', { 783 | file: that, 784 | fileData: file 785 | }]); 786 | }; 787 | 788 | // произошла ошибка 789 | xhr.onerror = function (event) { 790 | that.setStatus('stand_by'); 791 | fileUpPrivate.trigger(that._fileUp, 'error', ['load_error', { 792 | file: that, 793 | fileData: file, 794 | event: event 795 | }]); 796 | }; 797 | xhr.open(options.httpMethod || 'post', options.url, true); 798 | xhr.setRequestHeader('Cache-Control', 'no-cache'); 799 | xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 800 | fileUpPrivate.trigger(that._fileUp, 'load_before_start', [that, xhr]); 801 | if (window.FormData !== undefined) { 802 | var formData = new FormData(); 803 | formData.append(options.fieldName, file); 804 | if (Object.keys(options.extraFields).length) { 805 | $.each(options.extraFields, function (name, value) { 806 | formData.append(name, value); 807 | }); 808 | } 809 | return xhr.send(formData); 810 | } else { 811 | // IE 8,9 812 | return xhr.send(file); 813 | } 814 | }, 815 | /** 816 | * Отмена загрузки 817 | */ 818 | abort: function abort() { 819 | if (this._xhr) { 820 | this._xhr.abort(); 821 | } 822 | }, 823 | /** 824 | * Рендер элемента 825 | * @param {string} tpl 826 | * @return {string|null} 827 | */ 828 | render: function render(tpl) { 829 | if (!tpl || typeof tpl !== 'string') { 830 | return null; 831 | } 832 | var lang = this._fileUp.getLang(); 833 | var options = this._fileUp.getOptions(); 834 | var that = this; 835 | var isNoPreview = false; 836 | var mimeTypes = fileUpUtils.isObject(options.mimeTypes) ? options.mimeTypes : {}; 837 | var iconDefault = typeof options.iconDefault === 'string' ? options.iconDefault : ''; 838 | var showRemove = typeof options.showRemove === 'boolean' ? options.showRemove : true; 839 | var size = this.getSizeHuman(); 840 | var icon = null; 841 | var fileType = null; 842 | var fileExt = null; 843 | tpl = tpl.replace(/\[NAME\]/g, this.getName()); 844 | tpl = tpl.replace(/\[SIZE\]/g, size); 845 | tpl = tpl.replace(/\[UPLOAD\]/g, lang.upload); 846 | tpl = tpl.replace(/\[REMOVE\]/g, lang.remove); 847 | tpl = tpl.replace(/\[ABORT\]/g, lang.abort); 848 | if (this._file && this._file instanceof File) { 849 | if (this._file.type && typeof this._file.type === 'string' && this._file.type.match(/^image\/.*/)) { 850 | if (typeof FileReader !== 'undefined') { 851 | var reader = new FileReader(); 852 | reader.onload = function (ProgressEvent) { 853 | if (that._fileElement) { 854 | var preview = that._fileElement.find('.fileup-preview'); 855 | preview.removeClass('no-preview').find('img').attr('src', ProgressEvent.target.result); 856 | } 857 | }; 858 | reader.readAsDataURL(this._file); 859 | } 860 | isNoPreview = true; 861 | tpl = tpl.replace(/\[PREVIEW_SRC\]/g, ''); 862 | tpl = tpl.replace(/\[TYPE\]/g, 'fileup-image fileup-no-preview'); 863 | } else { 864 | tpl = tpl.replace(/\[PREVIEW_SRC\]/g, ''); 865 | tpl = tpl.replace(/\[TYPE\]/g, 'fileup-doc'); 866 | fileType = this._file.type; 867 | fileExt = this.getName().split('.').pop(); 868 | } 869 | } else { 870 | var urlPreview = this.getUrlPreview(); 871 | tpl = tpl.replace(/\[PREVIEW_SRC\]/g, urlPreview ? urlPreview : ''); 872 | tpl = tpl.replace(/\[TYPE\]/g, urlPreview ? 'fileup-image' : 'fileup-doc'); 873 | fileExt = this.getName() ? this.getName().split('.').pop().toLowerCase() : ''; 874 | } 875 | this._fileElement = $(tpl); 876 | if (isNoPreview) { 877 | this._fileElement.find('.fileup-preview').addClass('no-preview'); 878 | } 879 | if (!size) { 880 | this._fileElement.find('.fileup-size').hide(); 881 | } 882 | if (fileType || fileExt) { 883 | $.each(mimeTypes, function (name, type) { 884 | if (!fileUpUtils.isObject(type) || !type.hasOwnProperty('icon') || typeof type.icon !== 'string' || type.icon === '') { 885 | return; 886 | } 887 | if (fileType && type.hasOwnProperty('mime')) { 888 | if (typeof type.mime === 'string') { 889 | if (type.mime === fileType) { 890 | icon = type.icon; 891 | return false; 892 | } 893 | } else if (Array.isArray(type.mime)) { 894 | $.each(type.mime, function (key, mime) { 895 | if (typeof mime === 'string' && mime === fileType) { 896 | icon = type.icon; 897 | return false; 898 | } 899 | }); 900 | if (icon) { 901 | return false; 902 | } 903 | } else if (type.mime instanceof RegExp) { 904 | if (type.mime.test(fileType)) { 905 | icon = type.icon; 906 | return false; 907 | } 908 | } 909 | } 910 | if (fileExt && type.hasOwnProperty('ext') && Array.isArray(type.ext)) { 911 | $.each(type.ext, function (key, ext) { 912 | if (typeof ext === 'string' && ext === fileExt) { 913 | icon = type.icon; 914 | return false; 915 | } 916 | }); 917 | if (icon) { 918 | return false; 919 | } 920 | } 921 | }); 922 | } 923 | if (!icon) { 924 | icon = iconDefault; 925 | } 926 | this._fileElement.find('.fileup-icon').addClass(icon); 927 | if (!showRemove) { 928 | this._fileElement.find('.fileup-remove').hide(); 929 | } 930 | if (this.getUrlDownload()) { 931 | var $name = this._fileElement.find('.fileup-name'); 932 | if ($name[0]) { 933 | $name.replaceWith('' + this.getName() + ''); 934 | } 935 | } 936 | if (this._status === 'finish') { 937 | this._fileElement.find('.fileup-upload').hide(); 938 | this._fileElement.find('.fileup-abort').hide(); 939 | this._fileElement.find('.fileup-progress').hide(); 940 | } else { 941 | this._fileElement.find('.fileup-upload').click(function () { 942 | that.upload(); 943 | }); 944 | this._fileElement.find('.fileup-abort').click(function () { 945 | that.abort(); 946 | }); 947 | } 948 | this._fileElement.find('.fileup-remove').click(function () { 949 | that.remove(); 950 | }); 951 | return this._fileElement; 952 | } 953 | }; 954 | 955 | var tpl = Object.create(null); 956 | tpl['file.html'] = '
[NAME]
[NAME] ([SIZE])
[UPLOAD]
'; 957 | 958 | var fileUpInstance = { 959 | _options: { 960 | id: null, 961 | url: null, 962 | input: null, 963 | queue: null, 964 | dropzone: null, 965 | files: [], 966 | fieldName: 'file', 967 | extraFields: {}, 968 | lang: 'en', 969 | langItems: null, 970 | sizeLimit: 0, 971 | filesLimit: 0, 972 | httpMethod: 'post', 973 | timeout: null, 974 | autostart: false, 975 | showRemove: true, 976 | templateFile: null, 977 | onSelect: null, 978 | onRemove: null, 979 | onBeforeStart: null, 980 | onStart: null, 981 | onProgress: null, 982 | onAbort: null, 983 | onSuccess: null, 984 | onFinish: null, 985 | onError: null, 986 | onDragOver: null, 987 | onDragLeave: null, 988 | onDragEnd: null, 989 | onDragEnter: null, 990 | iconDefault: 'bi bi-file-earmark-text', 991 | mimeTypes: { 992 | archive: { 993 | mime: ['application/zip', 'application/gzip', 'application/x-bzip', 'application/x-bzip2', 'application/x-7z-compressed'], 994 | ext: ['zip', '7z', 'bz', 'bz2', 'gz', 'jar', 'rar', 'tar'], 995 | icon: 'bi bi-file-earmark-zip' 996 | }, 997 | word: { 998 | mime: ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 999 | ext: ['doc', 'docx'], 1000 | icon: 'bi bi-file-earmark-word' 1001 | }, 1002 | excel: { 1003 | mime: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], 1004 | ext: ['xls', 'xlsx'], 1005 | icon: 'bi bi-file-earmark-excel' 1006 | }, 1007 | image: { 1008 | mime: /image\/.*/, 1009 | ext: ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'raw', 'webp', 'heic', 'ico'], 1010 | icon: 'bi bi-file-earmark-image' 1011 | }, 1012 | video: { 1013 | mime: /video\/.*/, 1014 | ext: ['avi', 'mp4', 'mpeg', 'ogv', 'ts', 'webm', '3gp', '3g2', 'mkv'], 1015 | icon: 'bi bi-file-earmark-play' 1016 | }, 1017 | audio: { 1018 | mime: /audio\/.*/, 1019 | ext: ['avi', 'mp4', 'mpeg', 'ogv', 'ts', 'webm', '3gp', '3g2', 'mkv'], 1020 | icon: 'bi bi-file-earmark-music' 1021 | }, 1022 | pdf: { 1023 | mime: ['application/pdf'], 1024 | ext: ['pdf'], 1025 | icon: 'bi bi-file-earmark-pdf' 1026 | }, 1027 | binary: { 1028 | mime: ['application\/octet-stream'], 1029 | ext: ['bin', 'exe', 'dat', 'dll'], 1030 | icon: 'bi bi-file-earmark-binary' 1031 | } 1032 | } 1033 | }, 1034 | _id: null, 1035 | _fileUp: null, 1036 | _fileIndex: 0, 1037 | _input: null, 1038 | _queue: null, 1039 | _dropzone: null, 1040 | _files: {}, 1041 | _events: {}, 1042 | /** 1043 | * Инициализация 1044 | * @param {object} fileUp 1045 | * @param {object} options 1046 | * @private 1047 | */ 1048 | _init: function _init(fileUp, options) { 1049 | if (typeof options.url !== 'string' || !options.url) { 1050 | throw new Error('Dont set url param'); 1051 | } 1052 | this._fileUp = fileUp; 1053 | this._options = $.extend(true, {}, this._options, options); 1054 | this._id = typeof this._options.id === 'string' && this._options.id ? this._options.id : fileUpUtils.hashCode(); 1055 | if (!this._options.templateFile || typeof this._options.templateFile !== 'string') { 1056 | this._options.templateFile = tpl['file.html']; 1057 | } 1058 | fileUpPrivate.initInput(this); 1059 | fileUpPrivate.initQueue(this); 1060 | fileUpPrivate.initDropzone(this); 1061 | fileUpPrivate.initEvents(this); 1062 | fileUpPrivate.renderFiles(this); 1063 | }, 1064 | /** 1065 | * Разрушение экземпляра 1066 | */ 1067 | destruct: function destruct() { 1068 | var id = this.getId(); 1069 | if (!this._fileUp._instances.hasOwnProperty(id)) { 1070 | return; 1071 | } 1072 | delete this._fileUp._instances[id]; 1073 | }, 1074 | /** 1075 | * Получение параметров 1076 | * @returns {object} 1077 | */ 1078 | getOptions: function getOptions() { 1079 | return this._options; 1080 | }, 1081 | /** 1082 | * Получение id 1083 | * @return {string|null} 1084 | */ 1085 | getId: function getId() { 1086 | return this._id; 1087 | }, 1088 | /** 1089 | * Получение input 1090 | * @return {jQuery|null} 1091 | */ 1092 | getInput: function getInput() { 1093 | return this._input; 1094 | }, 1095 | /** 1096 | * Получение queue 1097 | * @return {jQuery|null} 1098 | */ 1099 | getQueue: function getQueue() { 1100 | return this._queue; 1101 | }, 1102 | /** 1103 | * Получение dropzone 1104 | * @return {jQuery|null} 1105 | */ 1106 | getDropzone: function getDropzone() { 1107 | return this._dropzone; 1108 | }, 1109 | /** 1110 | * Подписка на событие 1111 | * @param {string} eventName 1112 | * @param {function|string} callback 1113 | * @param {object|undefined} context 1114 | */ 1115 | on: function on(eventName, callback, context) { 1116 | if (_typeof(this._events[eventName]) !== 'object') { 1117 | this._events[eventName] = []; 1118 | } 1119 | this._events[eventName].push({ 1120 | context: context || this, 1121 | callback: callback, 1122 | singleExec: false 1123 | }); 1124 | }, 1125 | /** 1126 | * Подписка на событие таким образом, что выполнение произойдет лишь один раз 1127 | * @param {string} eventName 1128 | * @param {function|string} callback 1129 | * @param {object|undefined} context 1130 | */ 1131 | one: function one(eventName, callback, context) { 1132 | if (_typeof(this._events[eventName]) !== 'object') { 1133 | this._events[eventName] = []; 1134 | } 1135 | this._events[eventName].push({ 1136 | context: context || this, 1137 | callback: callback, 1138 | singleExec: true 1139 | }); 1140 | }, 1141 | /** 1142 | * Получение настроек языка 1143 | */ 1144 | getLang: function getLang() { 1145 | return $.extend(true, {}, this._options.langItems); 1146 | }, 1147 | /** 1148 | * Получение всех файлов 1149 | * @return {object} 1150 | */ 1151 | getFiles: function getFiles() { 1152 | return this._files; 1153 | }, 1154 | /** 1155 | * Получение файла по его id 1156 | * @param {int} fileId 1157 | * @return {object|null} 1158 | */ 1159 | getFileById: function getFileById(fileId) { 1160 | var result = null; 1161 | $.each(this._files, function (key, file) { 1162 | if (fileId === file.getId()) { 1163 | result = file; 1164 | } 1165 | }); 1166 | return result; 1167 | }, 1168 | /** 1169 | * Удаление всех файлов 1170 | */ 1171 | removeAll: function removeAll() { 1172 | $.each(this._files, function (key, file) { 1173 | file.remove(); 1174 | }); 1175 | }, 1176 | /** 1177 | * Загрузка всех файлов 1178 | */ 1179 | uploadAll: function uploadAll() { 1180 | $.each(this._files, function (key, file) { 1181 | file.upload(); 1182 | }); 1183 | }, 1184 | /** 1185 | * Отмена загрузки всех файлов 1186 | */ 1187 | abortAll: function abortAll() { 1188 | $.each(this._files, function (key, file) { 1189 | file.abort(); 1190 | }); 1191 | }, 1192 | /** 1193 | * Добавление файла в список из объекта File 1194 | * @param {object} file 1195 | * @result {boolean} 1196 | */ 1197 | appendFile: function appendFile(file) { 1198 | if (!(file instanceof File)) { 1199 | return false; 1200 | } 1201 | var fileInstance = $.extend(true, {}, fileUpFile); 1202 | var data = { 1203 | name: fileUpUtils.getFileName(file), 1204 | size: fileUpUtils.getFileSize(file), 1205 | type: file.type 1206 | }; 1207 | fileInstance._init(this, this._fileIndex, data, file); 1208 | this._files[this._fileIndex] = fileInstance; 1209 | var queue = this.getQueue(); 1210 | if (queue) { 1211 | queue.append(fileInstance.render(this._options.templateFile)); 1212 | } 1213 | this._fileIndex++; 1214 | if (typeof this._options.autostart === 'boolean' && this._options.autostart) { 1215 | fileInstance.upload(); 1216 | } 1217 | return true; 1218 | }, 1219 | /** 1220 | * Добавление файла в список из данных 1221 | * @param {object} data 1222 | * @result {boolean} 1223 | */ 1224 | appendFileByData: function appendFileByData(data) { 1225 | if (!fileUpUtils.isObject(data)) { 1226 | return false; 1227 | } 1228 | var fileInstance = $.extend(true, {}, fileUpFile); 1229 | fileInstance._init(this, this._fileIndex, data); 1230 | fileInstance.setStatus('finish'); 1231 | this._files[this._fileIndex] = fileInstance; 1232 | var queue = this.getQueue(); 1233 | if (queue) { 1234 | queue.append(fileInstance.render(this._options.templateFile)); 1235 | } 1236 | this._fileIndex++; 1237 | return true; 1238 | } 1239 | }; 1240 | 1241 | var fileUp = { 1242 | lang: {}, 1243 | _instances: {}, 1244 | /** 1245 | * Создание экземпляра 1246 | * @param {object} options 1247 | * @returns {object} 1248 | */ 1249 | create: function create(options) { 1250 | options = fileUpUtils.isObject(options) ? options : {}; 1251 | if (!options.hasOwnProperty('lang')) { 1252 | options.lang = 'en'; 1253 | } 1254 | var langList = this.lang.hasOwnProperty(options.lang) ? this.lang[options.lang] : {}; 1255 | options.langItems = options.hasOwnProperty('langItems') && fileUpUtils.isObject(options.langItems) ? $.extend(true, {}, langList, options.langItems) : langList; 1256 | var instance = $.extend(true, {}, fileUpInstance); 1257 | instance._init(this, options); 1258 | var id = instance.getId(); 1259 | this._instances[id] = instance; 1260 | return instance; 1261 | }, 1262 | /** 1263 | * Получение экземпляра по id 1264 | * @param {string} id 1265 | * @returns {object|null} 1266 | */ 1267 | get: function get(id) { 1268 | if (!this._instances.hasOwnProperty(id)) { 1269 | return null; 1270 | } 1271 | if (!$.contains(document, this._instances[id]._input[0])) { 1272 | delete this._instances[id]; 1273 | return null; 1274 | } 1275 | return this._instances[id]; 1276 | } 1277 | }; 1278 | 1279 | fileUp.lang.en = { 1280 | upload: 'Upload', 1281 | abort: 'Abort', 1282 | remove: 'Remove', 1283 | complete: 'Complete', 1284 | error: 'Error', 1285 | errorLoad: 'Error uploading file', 1286 | errorBadStatus: 'Error uploading file. Bad request.', 1287 | errorFilesLimit: 'The number of selected files exceeds the limit (%filesLimit%)', 1288 | errorSizeLimit: 'File "%fileName%" exceeds the size limit (%sizeLimit%)', 1289 | errorFileType: 'File "%fileName%" is incorrect', 1290 | errorOldBrowser: 'Your browser can not upload files. Update to the latest version' 1291 | }; 1292 | 1293 | fileUp.lang.ru = { 1294 | upload: 'Загрузить', 1295 | abort: 'Остановить', 1296 | remove: 'Удалить', 1297 | complete: 'Готово', 1298 | error: 'Ошибка', 1299 | errorLoad: 'Ошибка при загрузке файла', 1300 | errorBadStatus: 'Ошибка при загрузке файла. Некорректный запрос.', 1301 | errorFilesLimit: 'Количество выбранных файлов превышает лимит (%filesLimit%)', 1302 | errorSizeLimit: 'Файл "%fileName%" превышает предельный размер (%sizeLimit%)', 1303 | errorFileType: 'Файл "%fileName%" является некорректным', 1304 | errorOldBrowser: 'Обновите ваш браузер до последней версии' 1305 | }; 1306 | 1307 | fileUp.lang.es = { 1308 | upload: 'Subir', 1309 | abort: 'Cancelar', 1310 | remove: 'Eliminar', 1311 | complete: 'Cargado', 1312 | error: 'Error', 1313 | errorLoad: 'Error al cargar el archivo', 1314 | errorBadStatus: 'Error al cargar el archivo. Solicitud no válida.', 1315 | errorFilesLimit: 'El número de archivo selecccionados excede el límite (%filesLimit%)', 1316 | errorSizeLimit: 'El archivo "%fileName%" excede el limite de tamaño (%sizeLimit%)', 1317 | errorFileType: 'El archivo "%fileName%" es inválido', 1318 | errorOldBrowser: 'Tu navegador no puede subir archivos. Actualiza a la última versión' 1319 | }; 1320 | 1321 | fileUp.lang.pt = { 1322 | upload: 'Enviar', 1323 | abort: 'Cancelar', 1324 | remove: 'Remover', 1325 | complete: 'Enviado', 1326 | error: 'Erro', 1327 | errorLoad: 'Erro ao carregar o arquivo', 1328 | errorBadStatus: 'Erro ao carregar o arquivo. Pedido inválido.', 1329 | errorFilesLimit: 'O número de arquivos selecionados excede o limite (%filesLimit%)', 1330 | errorSizeLimit: 'Arquivo "%fileName%" excede o limite (%sizeLimit%)', 1331 | errorFileType: 'Arquivo "%fileName%" inválido', 1332 | errorOldBrowser: 'Seu navegador não pode enviar os arquivos. Atualize para a versão mais recente' 1333 | }; 1334 | 1335 | return fileUp; 1336 | 1337 | })); 1338 | -------------------------------------------------------------------------------- /dist/fileup.min.css: -------------------------------------------------------------------------------- 1 | .fileup-file{font-size:12px;width:350px;position:relative;overflow:hidden}.fileup-file.fileup-image .fileup-preview.no-preview{width:80px;height:56px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA4CAAAAABP3SBGAAACW0lEQVRYw+3Y3U8aQRAAcP7/98bb3YFDxSvUBCKlxigllVrFILFEmmJrxdYrB7e7Q+JfUD4Ocgcs7OX2oQ/OAwmZ8Mt+zO7NkcraZiOVSRuNTMp+BU2BzCIJAyIgK19eJIv6PoRB0njBRDHiBRYG6Rf0EwXvv/3fQM6Ngsjd5/GHKZCLdvkgV2wOhSmwRglj1Kp43AgoG9a05mCnKk2AvO+w4HjZj8IAKLsAwcmlLdQEl4oiCt6x+VVAGpogf3KFEhQPmWCEwNpSAZKLKHh0NNwwxhIJZuy4XAvEO2adqGctuzad7gm9RV8H5H6RpklVqMWOM74Arb2m9LVAbFFIA6lJteg2q6dXT9LXArlXmMwIyLla5CglCl8PxMZszWGlOKX+9RUC+XMuqDOg1xGx31Ev6wYQz6153QK0wkt7Qz4rRTUoe7uLg5Bm6a+jxU9ch9LToYwL4pkVesyyTAdDIwer/FfGA+XP+bmaXyjfZxn5y54krMMexgJFhURbAbp7P02JD7MEyXUxBojfAJaaC7r/gNPTGCSo3cYYIyzRlXaFOj3JB4eLBIPGasUrQLylsNoAkfzvl6vQVjH6iQstkA/e0XUtFSneH7DQdyDHntQB8Zqsb9JYdmngVukPbge5GxlHOGB1GR5wKyjrRL+zpHsd3AJyL09j9KoscxPqRdaDBRan+2WsLrhJcLzZZ0NhEpzcFa7cBDo7NGa8cX5w9S57teOTuFH5OHs0ry9sIeOHP9TpHJL32MlBLx8FL0c8UYhB9D0FCpX3yaKchei7Hk0a8PoCbgw0/a/IP7jBCOMW5IDRAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-size:contain}.fileup-file.fileup-image .fileup-preview.no-preview img{display:none}.fileup-file.fileup-image .fileup-preview .fileup-icon{display:none}.fileup-file.fileup-doc .fileup-preview img{display:none}.fileup-file .fileup-preview img{max-height:80px;max-width:80px}.fileup-file .fileup-description{padding-right:20px;word-break:break-all}.fileup-file .fileup-controls .fileup-remove{cursor:pointer;position:absolute;top:0;right:2px;width:16px;height:16px;font-size:16px;color:#9e9e9e}.fileup-file .fileup-controls .fileup-remove:hover{color:#616161}.fileup-file .fileup-controls .fileup-upload,.fileup-file .fileup-controls .fileup-abort{cursor:pointer}.fileup-file .fileup-result.fileup-success{color:#4cae4c}.fileup-file .fileup-result.fileup-error{color:#ce0000}.fileup-file .fileup-progress{height:3px}.fileup-file .fileup-progress-bar{width:1px;-webkit-transition:width .1s ease-out .1s;-moz-transition:width .1s ease-out .1s;-o-transition:width .1s ease-out .1s;transition:width .1s ease-out .1s}.fileup-btn{position:relative;overflow:hidden;display:inline-block}.fileup-btn input{position:absolute;top:0;right:0;margin:0;opacity:0;-ms-filter:"alpha(opacity=0)";font-size:200px;direction:ltr;cursor:pointer}.fileup-dropzone{background-color:#f6f6ff;border-width:1px;border-style:dashed;border-color:#0018ff;cursor:pointer}.fileup-dropzone.over{opacity:.7;border-style:solid} 2 | /*# sourceMappingURL=fileup.min.css.map */ 3 | -------------------------------------------------------------------------------- /dist/fileup.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["fileup.scss","_varaible.scss"],"names":[],"mappings":"AAAA,aACI,eACA,MCCmB,wBDCnB,gBAII,qDACI,WACA,YACA,65BACA,4BACA,wBAEA,yDACI,aAIR,uDACI,aAKJ,4CACI,aAIR,iCACI,gBACA,eAGJ,iCACI,mBACA,qBAIA,6CACI,eACA,kBACA,MACA,UACA,WACA,YACA,eACA,cAEJ,mDACI,cAGJ,yFAEI,eAMJ,2CACI,MChEW,QDkEf,yCACI,MClEW,QDsEnB,8BACI,WAEJ,kCACI,UACA,0CACA,uCACA,qCACA,kCAIR,YACI,kBACA,gBACA,qBAEA,kBACI,kBACA,MACA,QACA,SACA,UACA,8BACA,gBACA,cACA,eAIR,iBACI,iBClG2B,QDmG3B,aChG2B,IDiG3B,aClG2B,ODmG3B,aCpG2B,QDqG3B,eAEA,sBACI,WACA","file":"fileup.min.css","sourcesContent":[".fileup-file {\r\n font-size: 12px;\r\n width: $fileup-file-width;\r\n position: relative;\r\n overflow: hidden;\r\n\r\n &.fileup-image .fileup-preview {\r\n\r\n &.no-preview {\r\n width: 80px;\r\n height: 56px;\r\n background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA4CAAAAABP3SBGAAACW0lEQVRYw+3Y3U8aQRAAcP7/98bb3YFDxSvUBCKlxigllVrFILFEmmJrxdYrB7e7Q+JfUD4Ocgcs7OX2oQ/OAwmZ8Mt+zO7NkcraZiOVSRuNTMp+BU2BzCIJAyIgK19eJIv6PoRB0njBRDHiBRYG6Rf0EwXvv/3fQM6Ngsjd5/GHKZCLdvkgV2wOhSmwRglj1Kp43AgoG9a05mCnKk2AvO+w4HjZj8IAKLsAwcmlLdQEl4oiCt6x+VVAGpogf3KFEhQPmWCEwNpSAZKLKHh0NNwwxhIJZuy4XAvEO2adqGctuzad7gm9RV8H5H6RpklVqMWOM74Arb2m9LVAbFFIA6lJteg2q6dXT9LXArlXmMwIyLla5CglCl8PxMZszWGlOKX+9RUC+XMuqDOg1xGx31Ev6wYQz6153QK0wkt7Qz4rRTUoe7uLg5Bm6a+jxU9ch9LToYwL4pkVesyyTAdDIwer/FfGA+XP+bmaXyjfZxn5y54krMMexgJFhURbAbp7P02JD7MEyXUxBojfAJaaC7r/gNPTGCSo3cYYIyzRlXaFOj3JB4eLBIPGasUrQLylsNoAkfzvl6vQVjH6iQstkA/e0XUtFSneH7DQdyDHntQB8Zqsb9JYdmngVukPbge5GxlHOGB1GR5wKyjrRL+zpHsd3AJyL09j9KoscxPqRdaDBRan+2WsLrhJcLzZZ0NhEpzcFa7cBDo7NGa8cX5w9S57teOTuFH5OHs0ry9sIeOHP9TpHJL32MlBLx8FL0c8UYhB9D0FCpX3yaKchei7Hk0a8PoCbgw0/a/IP7jBCOMW5IDRAAAAAElFTkSuQmCC);\r\n background-repeat: no-repeat;\r\n background-size: contain;\r\n\r\n img {\r\n display: none;\r\n }\r\n }\r\n\r\n .fileup-icon {\r\n display: none;\r\n }\r\n }\r\n\r\n &.fileup-doc .fileup-preview {\r\n img {\r\n display: none;\r\n }\r\n }\r\n\r\n .fileup-preview img {\r\n max-height: 80px;\r\n max-width: 80px;\r\n }\r\n\r\n .fileup-description {\r\n padding-right: 20px;\r\n word-break: break-all;\r\n }\r\n\r\n .fileup-controls {\r\n .fileup-remove {\r\n cursor: pointer;\r\n position: absolute;\r\n top: 0;\r\n right: 2px;\r\n width: 16px;\r\n height: 16px;\r\n font-size: 16px;\r\n color: #9e9e9e;\r\n }\r\n .fileup-remove:hover {\r\n color: #616161;\r\n }\r\n\r\n .fileup-upload,\r\n .fileup-abort {\r\n cursor: pointer;\r\n }\r\n }\r\n\r\n\r\n .fileup-result {\r\n &.fileup-success {\r\n color: $fileup-color-success;\r\n }\r\n &.fileup-error {\r\n color: $fileup-color-error;\r\n }\r\n }\r\n\r\n .fileup-progress {\r\n height: 3px;\r\n }\r\n .fileup-progress-bar {\r\n width: 1px;\r\n -webkit-transition: width .1s ease-out 0.1s;\r\n -moz-transition: width .1s ease-out 0.1s;\r\n -o-transition: width .1s ease-out 0.1s;\r\n transition: width .1s ease-out 0.1s\r\n }\r\n}\r\n\r\n.fileup-btn {\r\n position: relative;\r\n overflow: hidden;\r\n display: inline-block;\r\n\r\n input {\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n margin: 0;\r\n opacity: 0;\r\n -ms-filter: 'alpha(opacity=0)';\r\n font-size: 200px;\r\n direction: ltr;\r\n cursor: pointer;\r\n }\r\n}\r\n\r\n.fileup-dropzone {\r\n background-color: $fileup-dropzone-bg-color;\r\n border-width: $fileup-dropzone-border-width;\r\n border-style:$fileup-dropzone-border-style;\r\n border-color: $fileup-dropzone-border-color;\r\n cursor: pointer;\r\n\r\n &.over {\r\n opacity: .7;\r\n border-style: solid;\r\n }\r\n}","\r\n$fileup-color-success: #4CAE4C !default;\r\n$fileup-color-error: #CE0000 !default;\r\n$fileup-file-width: 350px !default;\r\n\r\n$fileup-dropzone-bg-color: #f6f6ff !default;\r\n$fileup-dropzone-border-color: #0018ff !default;\r\n$fileup-dropzone-border-style: dashed !default;\r\n$fileup-dropzone-border-width: 1px !default;"]} -------------------------------------------------------------------------------- /dist/fileup.min.js: -------------------------------------------------------------------------------- 1 | !function(e,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(e="undefined"!=typeof globalThis?globalThis:e||self).fileUp=i()}(this,function(){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var d={isObject:function(e){return"object"===r(e)&&!Array.isArray(e)&&null!==e},isNumeric:function(e){return("number"==typeof e||"string"==typeof e&&""!==e.trim())&&!isNaN(e)},getFileSize:function(e){return e instanceof File?e.size||e.fileSize:null},getFileName:function(e){return e instanceof File?e.name||e.fileName:null},getSizeHuman:function(e){var i;return d.isNumeric(e)?(i="",1073741824<=(e=Number(e))?i=(e/1073741824).toFixed(2)+" Gb":1048576<=e?i=(e/1048576).toFixed(2)+" Mb":1024<=e?i=(e/1024).toFixed(2)+" Kb":0<=e&&(i=e+" bytes"),i):""},hashCode:function(){return this.crc32(((new Date).getTime()+Math.random()).toString()).toString(16)},crc32:function(e){for(var i=[],t=0;t<256;t++){for(var r=t,n=0;n<8;n++)r=1&r?3988292384^r>>>1:r>>>1;i[t]=r}for(var o=-1,s=0;s>>8^i[255&(o^e.charCodeAt(s))];return(-1^o)>>>0}},n=function(e){e=e.getElement();e&&(e.find(".fileup-upload").hide(),e.find(".fileup-abort").show(),e.find(".fileup-result").removeClass("fileup-error").removeClass("fileup-success").text(""))},o=function(e,i){i.lengthComputable&&(i=Math.ceil(i.loaded/i.total*100),e=e.getElement())&&e.find(".fileup-progress-bar").css("width",i+"%")},s=function(e){e=e.getElement();e&&(e.find(".fileup-abort").hide(),e.find(".fileup-upload").show(),e.find(".fileup-result").removeClass("fileup-error").removeClass("fileup-success").text(""))},l=function(e){var i,e=e.getElement();e&&(i=this.getLang(),e.find(".fileup-abort").hide(),e.find(".fileup-upload").hide(),e.find(".fileup-result").removeClass("fileup-error").addClass("fileup-success").text(i.complete))},a=function(e,i){var t=this.getLang();switch(e){case"files_limit":alert(t.errorFilesLimit.replace(/%filesLimit%/g,i.filesLimit));break;case"size_limit":var r=d.getSizeHuman(i.sizeLimit),n=t.errorSizeLimit;n=(n=n.replace(/%sizeLimit%/g,r)).replace(/%fileName%/g,d.getFileName(i.fileData)),alert(n);break;case"file_type":alert(t.errorFileType.replace(/%fileName%/g,d.getFileName(i.fileData)));break;case"load_bad_status":case"load_error":case"load_timeout":r=i.file.getElement();r&&(n="load_bad_status"===e?t.errorBadStatus:t.errorLoad,r.find(".fileup-abort").hide(),r.find(".fileup-upload").show(),r.find(".fileup-result").addClass("fileup-error").text(n));break;case"old_browser":alert(t.errorOldBrowser)}},f=function(e){e.stopPropagation(),e.preventDefault(),e.dataTransfer.dropEffect="copy";e=this.getDropzone();e&&e.addClass("over")},u=function(e){var i=this.getDropzone();i&&i.removeClass("over")},p=function(e){var i=this.getDropzone();i&&i.removeClass("over")},c=function(e){e.stopPropagation(),e.preventDefault(),e.dataTransfer.dropEffect="copy"},m={initInput:function(e){var i=null;if(e._options.input instanceof HTMLElement||e._options.input instanceof jQuery?i=$(e._options.input):"string"==typeof e._options.input&&e._options.input&&(i=$("#"+e._options.input)),!i||!$(i)[0]||"file"!==$(i)[0].type)throw new Error("Not found input element");e._input=i},initQueue:function(e){var i=null;if(e._options.queue instanceof HTMLElement||e._options.queue instanceof jQuery?i=$(e._options.queue):"string"==typeof e._options.queue&&e._options.queue&&(i=$("#"+e._options.queue)),!i||!$(i)[0])throw new Error("Not found queue element");e._queue=i},initDropzone:function(i){var t,e=null;i._options.dropzone instanceof HTMLElement||i._options.dropzone instanceof jQuery?e=$(i._options.dropzone):"string"==typeof i._options.dropzone&&i._options.dropzone&&(e=$("#"+i._options.dropzone)),e&&(i._dropzone=e,t=this,e.on("click",function(){i.getInput().click()}),e[0].addEventListener("dragover",function(e){t.trigger(i,"drag_over",[e])}),e[0].addEventListener("dragleave",function(e){t.trigger(i,"drag_leave",[e])}),e[0].addEventListener("dragenter",function(e){t.trigger(i,"drag_enter",[e])}),e[0].addEventListener("dragend",function(e){t.trigger(i,"drag_end",[e])}),e[0].addEventListener("drop",function(e){i.getInput()[0].files=e.target.files||e.dataTransfer.files,t.appendFiles(i,e)}))},initEvents:function(r){function e(e,i){var t=null;"function"==typeof i?t=i:"string"==typeof i&&(t=new Function(i)),t&&r.on(e,t)}var i=r.getOptions(),t=this;e("load_start",n),e("load_progress",o),e("load_abort",s),e("load_success",l),e("error",a),e("drag_over",f),e("drag_leave",c),e("drag_end",u),e("drag_enter",p),i.onSelect&&e("select",i.onSelect),i.onRemove&&e("remove",i.onRemove),i.onBeforeStart&&e("load_before_start",i.onBeforeStart),i.onStart&&e("load_start",i.onStart),i.onProgress&&e("load_progress",i.onProgress),i.onAbort&&e("load_abort",i.onAbort),i.onSuccess&&e("load_success",i.onSuccess),i.onFinish&&e("load_finish",i.onFinish),i.onError&&e("error",i.onError),i.onDragOver&&e("drag_over",i.onDragOver),i.onDragLeave&&e("drag_leave",i.onDragLeave),i.onDragEnd&&e("drag_end",i.onDragEnd),i.onDragEnter&&e("drag_enter",i.onDragEnter),r.getInput().on("change",function(e){t.appendFiles(r,e)})},renderFiles:function(e){var i=e.getOptions();if(Array.isArray(i.files)&&0l.sizeLimit)return s.trigger(o,"error",["size_limit",{fileData:e,sizeLimit:l.sizeLimit}]),0;if(0=l.filesLimit)return s.trigger(o,"error",["files_limit",{fileData:e,filesLimit:l.filesLimit}]),1;if("string"==typeof a[0].accept){var i=a[0].accept;if(i&&/[^\w]+/.test(i)){var t=!1,r=i.split(",");if(0'+this.getName()+""),"finish"===this._status?(this._fileElement.find(".fileup-upload").hide(),this._fileElement.find(".fileup-abort").hide(),this._fileElement.find(".fileup-progress").hide()):(this._fileElement.find(".fileup-upload").click(function(){i.upload()}),this._fileElement.find(".fileup-abort").click(function(){i.abort()})),this._fileElement.find(".fileup-remove").click(function(){i.remove()}),this._fileElement):null}},t=Object.create(null),h={_options:{id:null,url:null,input:null,queue:null,dropzone:null,files:[],fieldName:"file",extraFields:{},lang:"en",langItems:null,sizeLimit:0,filesLimit:0,httpMethod:"post",timeout:null,autostart:!(t["file.html"]='
[NAME]
[NAME] ([SIZE])
[UPLOAD]
'),showRemove:!0,templateFile:null,onSelect:null,onRemove:null,onBeforeStart:null,onStart:null,onProgress:null,onAbort:null,onSuccess:null,onFinish:null,onError:null,onDragOver:null,onDragLeave:null,onDragEnd:null,onDragEnter:null,iconDefault:"bi bi-file-earmark-text",mimeTypes:{archive:{mime:["application/zip","application/gzip","application/x-bzip","application/x-bzip2","application/x-7z-compressed"],ext:["zip","7z","bz","bz2","gz","jar","rar","tar"],icon:"bi bi-file-earmark-zip"},word:{mime:["application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"],ext:["doc","docx"],icon:"bi bi-file-earmark-word"},excel:{mime:["application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],ext:["xls","xlsx"],icon:"bi bi-file-earmark-excel"},image:{mime:/image\/.*/,ext:["jpg","jpeg","png","bmp","gif","raw","webp","heic","ico"],icon:"bi bi-file-earmark-image"},video:{mime:/video\/.*/,ext:["avi","mp4","mpeg","ogv","ts","webm","3gp","3g2","mkv"],icon:"bi bi-file-earmark-play"},audio:{mime:/audio\/.*/,ext:["avi","mp4","mpeg","ogv","ts","webm","3gp","3g2","mkv"],icon:"bi bi-file-earmark-music"},pdf:{mime:["application/pdf"],ext:["pdf"],icon:"bi bi-file-earmark-pdf"},binary:{mime:["application/octet-stream"],ext:["bin","exe","dat","dll"],icon:"bi bi-file-earmark-binary"}}},_id:null,_fileUp:null,_fileIndex:0,_input:null,_queue:null,_dropzone:null,_files:{},_events:{},_init:function(e,i){if("string"!=typeof i.url||!i.url)throw new Error("Dont set url param");this._fileUp=e,this._options=$.extend(!0,{},this._options,i),this._id="string"==typeof this._options.id&&this._options.id?this._options.id:d.hashCode(),this._options.templateFile&&"string"==typeof this._options.templateFile||(this._options.templateFile=t["file.html"]),m.initInput(this),m.initQueue(this),m.initDropzone(this),m.initEvents(this),m.renderFiles(this)},destruct:function(){var e=this.getId();this._fileUp._instances.hasOwnProperty(e)&&delete this._fileUp._instances[e]},getOptions:function(){return this._options},getId:function(){return this._id},getInput:function(){return this._input},getQueue:function(){return this._queue},getDropzone:function(){return this._dropzone},on:function(e,i,t){"object"!==r(this._events[e])&&(this._events[e]=[]),this._events[e].push({context:t||this,callback:i,singleExec:!1})},one:function(e,i,t){"object"!==r(this._events[e])&&(this._events[e]=[]),this._events[e].push({context:t||this,callback:i,singleExec:!0})},getLang:function(){return $.extend(!0,{},this._options.langItems)},getFiles:function(){return this._files},getFileById:function(t){var r=null;return $.each(this._files,function(e,i){t===i.getId()&&(r=i)}),r},removeAll:function(){$.each(this._files,function(e,i){i.remove()})},uploadAll:function(){$.each(this._files,function(e,i){i.upload()})},abortAll:function(){$.each(this._files,function(e,i){i.abort()})},appendFile:function(e){var i,t;return e instanceof File&&(i=$.extend(!0,{},g),t={name:d.getFileName(e),size:d.getFileSize(e),type:e.type},i._init(this,this._fileIndex,t,e),this._files[this._fileIndex]=i,(t=this.getQueue())&&t.append(i.render(this._options.templateFile)),this._fileIndex++,"boolean"==typeof this._options.autostart&&this._options.autostart&&i.upload(),!0)},appendFileByData:function(e){var i;return!!d.isObject(e)&&((i=$.extend(!0,{},g))._init(this,this._fileIndex,e),i.setStatus("finish"),this._files[this._fileIndex]=i,(e=this.getQueue())&&e.append(i.render(this._options.templateFile)),this._fileIndex++,!0)}},e={lang:{},_instances:{},create:function(e){(e=d.isObject(e)?e:{}).hasOwnProperty("lang")||(e.lang="en");var i=this.lang.hasOwnProperty(e.lang)?this.lang[e.lang]:{},i=(e.langItems=e.hasOwnProperty("langItems")&&d.isObject(e.langItems)?$.extend(!0,{},i,e.langItems):i,$.extend(!0,{},h)),e=(i._init(this,e),i.getId());return this._instances[e]=i},get:function(e){return this._instances.hasOwnProperty(e)?$.contains(document,this._instances[e]._input[0])?this._instances[e]:(delete this._instances[e],null):null}};return e.lang.en={upload:"Upload",abort:"Abort",remove:"Remove",complete:"Complete",error:"Error",errorLoad:"Error uploading file",errorBadStatus:"Error uploading file. Bad request.",errorFilesLimit:"The number of selected files exceeds the limit (%filesLimit%)",errorSizeLimit:'File "%fileName%" exceeds the size limit (%sizeLimit%)',errorFileType:'File "%fileName%" is incorrect',errorOldBrowser:"Your browser can not upload files. Update to the latest version"},e.lang.ru={upload:"Загрузить",abort:"Остановить",remove:"Удалить",complete:"Готово",error:"Ошибка",errorLoad:"Ошибка при загрузке файла",errorBadStatus:"Ошибка при загрузке файла. Некорректный запрос.",errorFilesLimit:"Количество выбранных файлов превышает лимит (%filesLimit%)",errorSizeLimit:'Файл "%fileName%" превышает предельный размер (%sizeLimit%)',errorFileType:'Файл "%fileName%" является некорректным',errorOldBrowser:"Обновите ваш браузер до последней версии"},e.lang.es={upload:"Subir",abort:"Cancelar",remove:"Eliminar",complete:"Cargado",error:"Error",errorLoad:"Error al cargar el archivo",errorBadStatus:"Error al cargar el archivo. Solicitud no válida.",errorFilesLimit:"El número de archivo selecccionados excede el límite (%filesLimit%)",errorSizeLimit:'El archivo "%fileName%" excede el limite de tamaño (%sizeLimit%)',errorFileType:'El archivo "%fileName%" es inválido',errorOldBrowser:"Tu navegador no puede subir archivos. Actualiza a la última versión"},e.lang.pt={upload:"Enviar",abort:"Cancelar",remove:"Remover",complete:"Enviado",error:"Erro",errorLoad:"Erro ao carregar o arquivo",errorBadStatus:"Erro ao carregar o arquivo. Pedido inválido.",errorFilesLimit:"O número de arquivos selecionados excede o limite (%filesLimit%)",errorSizeLimit:'Arquivo "%fileName%" excede o limite (%sizeLimit%)',errorFileType:'Arquivo "%fileName%" inválido',errorOldBrowser:"Seu navegador não pode enviar os arquivos. Atualize para a versão mais recente"},e}); 2 | //# sourceMappingURL=fileup.min.js.map 3 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const concat = require('gulp-concat'); 3 | const sourcemaps = require('gulp-sourcemaps'); 4 | const uglify = require('gulp-uglify'); 5 | const htmlToJs = require('gulp-html-to-js'); 6 | const wrapFile = require('gulp-wrap-file'); 7 | const sass = require('gulp-sass')(require('sass')); 8 | const rollup = require('@rollup/stream'); 9 | const rollupSourcemaps = require('rollup-plugin-sourcemaps'); 10 | const rollupBabel = require('@rollup/plugin-babel'); 11 | const nodeResolve = require('@rollup/plugin-node-resolve'); 12 | const source = require('vinyl-source-stream'); 13 | const buffer = require("vinyl-buffer"); 14 | 15 | 16 | var conf = { 17 | dist: "./dist", 18 | js: { 19 | file: 'fileup.js', 20 | fileMin: 'fileup.min.js', 21 | main: 'src/js/main.js', 22 | src: 'src/js/**/*.js' 23 | }, 24 | css: { 25 | fileMin: 'fileup.min.css', 26 | file: 'fileup.css', 27 | main: 'src/css/main.scss', 28 | src: [ 29 | 'src/css/**/*.scss', 30 | ] 31 | }, 32 | css_bootstrap: { 33 | fileMin: 'fileup.bootstrap.min.css', 34 | main: 'src/css/fileup.bootstrap.scss', 35 | }, 36 | tpl: { 37 | file: 'fileup.templates.js', 38 | dist: './src/js', 39 | src: [ 40 | 'src/html/**/*.html', 41 | 'src/html/*.html' 42 | ] 43 | } 44 | }; 45 | 46 | 47 | 48 | gulp.task('build_css_min', function(){ 49 | return gulp.src(conf.css.main) 50 | .pipe(sourcemaps.init()) 51 | .pipe(sass({includePaths: ['node_modules'], outputStyle: 'compressed'}).on('error', sass.logError)) 52 | .pipe(concat(conf.css.fileMin)) 53 | .pipe(sourcemaps.write('.')) 54 | .pipe(gulp.dest(conf.dist)); 55 | }); 56 | 57 | gulp.task('build_css_min_fast', function(){ 58 | return gulp.src(conf.css.main) 59 | .pipe(sass({includePaths: ['node_modules']}).on('error', sass.logError)) 60 | .pipe(concat(conf.css.fileMin)) 61 | .pipe(gulp.dest(conf.dist)); 62 | }); 63 | 64 | gulp.task('build_css', function(){ 65 | return gulp.src(conf.css.main) 66 | .pipe(sass({includePaths: ['node_modules']}).on('error', sass.logError)) 67 | .pipe(concat(conf.css.file)) 68 | .pipe(gulp.dest(conf.dist)); 69 | }); 70 | 71 | 72 | 73 | gulp.task('build_js', function() { 74 | return rollup({ 75 | input: conf.js.main, 76 | output: { 77 | sourcemap: false, 78 | format: 'umd', 79 | name: "fileUp" 80 | }, 81 | onwarn: function (log, handler) { 82 | if (log.code === 'CIRCULAR_DEPENDENCY') { 83 | return; // Ignore circular dependency warnings 84 | } 85 | handler(log.message); 86 | }, 87 | context: "window", 88 | plugins: [ 89 | nodeResolve(), 90 | rollupBabel({babelHelpers: 'bundled'}), 91 | ] 92 | }) 93 | .pipe(source(conf.js.file)) 94 | .pipe(buffer()) 95 | .pipe(gulp.dest(conf.dist)); 96 | }); 97 | 98 | gulp.task('build_js_min_fast', function() { 99 | return rollup({ 100 | input: conf.js.main, 101 | output: { 102 | sourcemap: false, 103 | format: 'umd', 104 | name: "fileUp" 105 | }, 106 | onwarn: function (log, handler) { 107 | if (log.code === 'CIRCULAR_DEPENDENCY') { 108 | return; // Ignore circular dependency warnings 109 | } 110 | handler(log.message); 111 | }, 112 | context: "window", 113 | plugins: [ 114 | nodeResolve(), 115 | rollupSourcemaps(), 116 | rollupBabel({babelHelpers: 'bundled'}), 117 | ] 118 | }) 119 | .pipe(source(conf.js.fileMin)) 120 | .pipe(buffer()) 121 | .pipe(gulp.dest(conf.dist)); 122 | }); 123 | 124 | 125 | gulp.task('build_js_min', function() { 126 | return rollup({ 127 | input: conf.js.main, 128 | output: { 129 | sourcemap: false, 130 | format: 'umd', 131 | name: "fileUp" 132 | }, 133 | onwarn: function (log, handler) { 134 | if (log.code === 'CIRCULAR_DEPENDENCY') { 135 | return; // Ignore circular dependency warnings 136 | } 137 | handler(log.message); 138 | }, 139 | context: "window", 140 | plugins: [ 141 | nodeResolve(), 142 | rollupSourcemaps(), 143 | rollupBabel({babelHelpers: 'bundled'}), 144 | ] 145 | }) 146 | .pipe(source(conf.js.fileMin)) 147 | .pipe(buffer()) 148 | .pipe(sourcemaps.init()) 149 | .pipe(uglify()) 150 | .pipe(sourcemaps.write('.')) 151 | .pipe(gulp.dest(conf.dist)); 152 | }); 153 | 154 | 155 | gulp.task('build_tpl', function() { 156 | return gulp.src(conf.tpl.src) 157 | .pipe(htmlToJs({global: 'tpl', concat: conf.tpl.file})) 158 | .pipe(wrapFile({ 159 | wrapper: function(content, file) { 160 | content = content.replace(/\\n/g, ' '); 161 | content = content.replace(/[ ]{2,}/g, ' '); 162 | return 'let ' + content + ";\nexport default tpl;" 163 | } 164 | })) 165 | .pipe(gulp.dest(conf.tpl.dist)); 166 | }); 167 | 168 | 169 | gulp.task('build_bootstrap', function() { 170 | return gulp.src(conf.css_bootstrap.main) 171 | .pipe(sourcemaps.init()) 172 | .pipe(sass({includePaths: ['node_modules'], outputStyle: 'compressed'}).on('error', sass.logError)) 173 | .pipe(concat(conf.css_bootstrap.fileMin)) 174 | .pipe(sourcemaps.write('.')) 175 | .pipe(gulp.dest(conf.dist)); 176 | }); 177 | 178 | 179 | 180 | gulp.task('build_watch', function() { 181 | gulp.watch(conf.tpl.src, gulp.series(['build_tpl', 'build_js_min_fast'])); 182 | gulp.watch(conf.js.src, gulp.parallel(['build_js_min_fast'])); 183 | gulp.watch(conf.css.src, gulp.parallel(['build_css_min_fast'])); 184 | }); 185 | 186 | gulp.task("default", gulp.series([ 'build_tpl', 'build_js_min', 'build_js'])); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fileup-js", 3 | "version": "1.0.3", 4 | "description": "Fileup - file upload lib", 5 | "scripts": { 6 | "build_all": "gulp build_tpl && gulp build_js_min && gulp build_js && gulp build_css_min && gulp build_css && npm run test_js", 7 | 8 | "build_tpl": "gulp build_tpl", 9 | "build_js": "gulp build_tpl && gulp build_js", 10 | "build_js_min": "gulp build_tpl && gulp build_js_min", 11 | 12 | "build_css": "gulp build_css", 13 | "build_css_min": "gulp build_css_min", 14 | 15 | "build_watch": "gulp build_watch", 16 | "build_bootstrap": "gulp build_bootstrap", 17 | "test_js": "eslint -c .eslintrc.json --max-warnings 100 --ext .js ./dist" 18 | }, 19 | "devDependencies": { 20 | "gulp": "5.0.0", 21 | "gulp-concat": "2.6.1", 22 | "gulp-sourcemaps": "3.0.0", 23 | "gulp-sass": "5.1.0", 24 | "gulp-uglify": "3.0.2", 25 | "gulp-wrap-file": "0.1.2", 26 | "gulp-html-to-js": "0.0.7", 27 | "@rollup/stream": "3.0.1", 28 | "rollup-plugin-sourcemaps": "0.6.3", 29 | "@rollup/plugin-babel": "6.0.4", 30 | "@rollup/plugin-node-resolve": "15.2.3", 31 | "vinyl-source-stream": "2.0.0", 32 | "vinyl-buffer": "1.0.1", 33 | "sass": "1.76.0", 34 | "eslint": "8.57.0", 35 | "@babel/plugin-transform-template-literals": "7.24.1", 36 | "@babel/preset-env": "7.24.5", 37 | "bootstrap": "5.3.3" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/n2ref/fileup.git" 42 | }, 43 | "keywords": [ 44 | "fileup", 45 | "js", 46 | "javascript", 47 | "frontend", 48 | "libriary", 49 | "lib", 50 | "form", 51 | "file", 52 | "upload", 53 | "bootstrap" 54 | ], 55 | "author": "n2ref", 56 | "license": "ISC", 57 | "bugs": { 58 | "url": "https://github.com/n2ref/fileup/issues" 59 | }, 60 | "homepage": "https://github.com/n2ref/fileup" 61 | } 62 | -------------------------------------------------------------------------------- /src/css/_varaible.scss: -------------------------------------------------------------------------------- 1 | 2 | $fileup-color-success: #4CAE4C !default; 3 | $fileup-color-error: #CE0000 !default; 4 | $fileup-file-width: 350px !default; 5 | 6 | $fileup-dropzone-bg-color: #f6f6ff !default; 7 | $fileup-dropzone-border-color: #0018ff !default; 8 | $fileup-dropzone-border-style: dashed !default; 9 | $fileup-dropzone-border-width: 1px !default; -------------------------------------------------------------------------------- /src/css/fileup.bootstrap.scss: -------------------------------------------------------------------------------- 1 | 2 | .fileup-file { 3 | 4 | @import "bootstrap/scss/functions"; 5 | @import "bootstrap/scss/variables"; 6 | @import "bootstrap/scss/mixins"; 7 | @import "bootstrap/scss/maps"; 8 | @import "bootstrap/scss/progress"; 9 | @import "bootstrap/scss/helpers/colored-links"; 10 | @import "bootstrap/scss/utilities"; 11 | @import "bootstrap/scss/utilities/api"; 12 | } -------------------------------------------------------------------------------- /src/css/fileup.scss: -------------------------------------------------------------------------------- 1 | .fileup-file { 2 | font-size: 12px; 3 | width: $fileup-file-width; 4 | position: relative; 5 | overflow: hidden; 6 | 7 | &.fileup-image .fileup-preview { 8 | 9 | &.no-preview { 10 | width: 80px; 11 | height: 56px; 12 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA4CAAAAABP3SBGAAACW0lEQVRYw+3Y3U8aQRAAcP7/98bb3YFDxSvUBCKlxigllVrFILFEmmJrxdYrB7e7Q+JfUD4Ocgcs7OX2oQ/OAwmZ8Mt+zO7NkcraZiOVSRuNTMp+BU2BzCIJAyIgK19eJIv6PoRB0njBRDHiBRYG6Rf0EwXvv/3fQM6Ngsjd5/GHKZCLdvkgV2wOhSmwRglj1Kp43AgoG9a05mCnKk2AvO+w4HjZj8IAKLsAwcmlLdQEl4oiCt6x+VVAGpogf3KFEhQPmWCEwNpSAZKLKHh0NNwwxhIJZuy4XAvEO2adqGctuzad7gm9RV8H5H6RpklVqMWOM74Arb2m9LVAbFFIA6lJteg2q6dXT9LXArlXmMwIyLla5CglCl8PxMZszWGlOKX+9RUC+XMuqDOg1xGx31Ev6wYQz6153QK0wkt7Qz4rRTUoe7uLg5Bm6a+jxU9ch9LToYwL4pkVesyyTAdDIwer/FfGA+XP+bmaXyjfZxn5y54krMMexgJFhURbAbp7P02JD7MEyXUxBojfAJaaC7r/gNPTGCSo3cYYIyzRlXaFOj3JB4eLBIPGasUrQLylsNoAkfzvl6vQVjH6iQstkA/e0XUtFSneH7DQdyDHntQB8Zqsb9JYdmngVukPbge5GxlHOGB1GR5wKyjrRL+zpHsd3AJyL09j9KoscxPqRdaDBRan+2WsLrhJcLzZZ0NhEpzcFa7cBDo7NGa8cX5w9S57teOTuFH5OHs0ry9sIeOHP9TpHJL32MlBLx8FL0c8UYhB9D0FCpX3yaKchei7Hk0a8PoCbgw0/a/IP7jBCOMW5IDRAAAAAElFTkSuQmCC); 13 | background-repeat: no-repeat; 14 | background-size: contain; 15 | 16 | img { 17 | display: none; 18 | } 19 | } 20 | 21 | .fileup-icon { 22 | display: none; 23 | } 24 | } 25 | 26 | &.fileup-doc .fileup-preview { 27 | img { 28 | display: none; 29 | } 30 | } 31 | 32 | .fileup-preview img { 33 | max-height: 80px; 34 | max-width: 80px; 35 | } 36 | 37 | .fileup-description { 38 | padding-right: 20px; 39 | word-break: break-all; 40 | } 41 | 42 | .fileup-controls { 43 | .fileup-remove { 44 | cursor: pointer; 45 | position: absolute; 46 | top: 0; 47 | right: 2px; 48 | width: 16px; 49 | height: 16px; 50 | font-size: 16px; 51 | color: #9e9e9e; 52 | } 53 | .fileup-remove:hover { 54 | color: #616161; 55 | } 56 | 57 | .fileup-upload, 58 | .fileup-abort { 59 | cursor: pointer; 60 | } 61 | } 62 | 63 | 64 | .fileup-result { 65 | &.fileup-success { 66 | color: $fileup-color-success; 67 | } 68 | &.fileup-error { 69 | color: $fileup-color-error; 70 | } 71 | } 72 | 73 | .fileup-progress { 74 | height: 3px; 75 | } 76 | .fileup-progress-bar { 77 | width: 1px; 78 | -webkit-transition: width .1s ease-out 0.1s; 79 | -moz-transition: width .1s ease-out 0.1s; 80 | -o-transition: width .1s ease-out 0.1s; 81 | transition: width .1s ease-out 0.1s 82 | } 83 | } 84 | 85 | .fileup-btn { 86 | position: relative; 87 | overflow: hidden; 88 | display: inline-block; 89 | 90 | input { 91 | position: absolute; 92 | top: 0; 93 | right: 0; 94 | margin: 0; 95 | opacity: 0; 96 | -ms-filter: 'alpha(opacity=0)'; 97 | font-size: 200px; 98 | direction: ltr; 99 | cursor: pointer; 100 | } 101 | } 102 | 103 | .fileup-dropzone { 104 | background-color: $fileup-dropzone-bg-color; 105 | border-width: $fileup-dropzone-border-width; 106 | border-style:$fileup-dropzone-border-style; 107 | border-color: $fileup-dropzone-border-color; 108 | cursor: pointer; 109 | 110 | &.over { 111 | opacity: .7; 112 | border-style: solid; 113 | } 114 | } -------------------------------------------------------------------------------- /src/css/main.scss: -------------------------------------------------------------------------------- 1 | 2 | @import "_varaible"; 3 | @import "fileup"; -------------------------------------------------------------------------------- /src/html/file.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | [NAME] 4 | 5 |
6 |
7 |
8 | [NAME] 9 | ([SIZE]) 10 |
11 | 12 |
13 | 14 | [UPLOAD] 15 | 16 |
17 | 18 |
19 | 20 |
21 |
22 |
23 |
24 |
-------------------------------------------------------------------------------- /src/js/fileup.events.js: -------------------------------------------------------------------------------- 1 | import fileUpUtils from "./fileup.utils"; 2 | 3 | let fileUpEvents = { 4 | 5 | /** 6 | * Событие начала загрузки 7 | * @param {object} file 8 | */ 9 | onLoadStart: function(file) { 10 | 11 | let $file = file.getElement(); 12 | 13 | if ($file) { 14 | $file.find('.fileup-upload').hide(); 15 | $file.find('.fileup-abort').show(); 16 | 17 | $file.find('.fileup-result') 18 | .removeClass('fileup-error') 19 | .removeClass('fileup-success') 20 | .text(''); 21 | } 22 | }, 23 | 24 | 25 | /** 26 | * Событие начала изменения прогресса загрузки 27 | * @param {object} file 28 | * @param {ProgressEvent} ProgressEvent 29 | */ 30 | onLoadProgress: function(file, ProgressEvent) { 31 | 32 | if (ProgressEvent.lengthComputable) { 33 | let percent = Math.ceil(ProgressEvent.loaded / ProgressEvent.total * 100); 34 | let $file = file.getElement(); 35 | 36 | if ($file) { 37 | $file.find('.fileup-progress-bar').css('width', percent + "%"); 38 | } 39 | } 40 | }, 41 | 42 | 43 | /** 44 | * Событие начала загрузки 45 | * @param {object} file 46 | */ 47 | onLoadAbort: function(file) { 48 | 49 | let $file = file.getElement(); 50 | 51 | if ($file) { 52 | $file.find('.fileup-abort').hide(); 53 | $file.find('.fileup-upload').show(); 54 | $file.find('.fileup-result') 55 | .removeClass('fileup-error') 56 | .removeClass('fileup-success') 57 | .text(''); 58 | } 59 | }, 60 | 61 | 62 | /** 63 | * Событие успешной загрузки файла 64 | * @param {object} file 65 | */ 66 | onSuccess: function(file) { 67 | 68 | let $file = file.getElement(); 69 | 70 | if ($file) { 71 | let lang = this.getLang(); 72 | 73 | $file.find('.fileup-abort').hide(); 74 | $file.find('.fileup-upload').hide(); 75 | $file.find('.fileup-result') 76 | .removeClass('fileup-error') 77 | .addClass('fileup-success') 78 | .text(lang.complete); 79 | } 80 | }, 81 | 82 | 83 | /** 84 | * Событие ошибки 85 | * @param {string} eventName 86 | * @param {object} options 87 | */ 88 | onError: function(eventName, options) { 89 | 90 | let lang = this.getLang(); 91 | 92 | switch(eventName) { 93 | case 'files_limit': 94 | alert(lang.errorFilesLimit.replace(/%filesLimit%/g, options.filesLimit)); 95 | break; 96 | 97 | case 'size_limit': 98 | let size = fileUpUtils.getSizeHuman(options.sizeLimit); 99 | let message = lang.errorSizeLimit; 100 | 101 | message = message.replace(/%sizeLimit%/g, size); 102 | message = message.replace(/%fileName%/g, fileUpUtils.getFileName(options.fileData)); 103 | 104 | alert(message); 105 | break; 106 | 107 | case 'file_type': 108 | alert(lang.errorFileType.replace(/%fileName%/g, fileUpUtils.getFileName(options.fileData))); 109 | break; 110 | 111 | case 'load_bad_status': 112 | case 'load_error': 113 | case 'load_timeout': 114 | let $file = options.file.getElement(); 115 | 116 | if ($file) { 117 | let message = eventName === 'load_bad_status' 118 | ? lang.errorBadStatus 119 | : lang.errorLoad; 120 | 121 | $file.find('.fileup-abort').hide(); 122 | $file.find('.fileup-upload').show(); 123 | $file.find('.fileup-result') 124 | .addClass('fileup-error') 125 | .text(message); 126 | } 127 | break; 128 | 129 | case 'old_browser': 130 | alert(lang.errorOldBrowser); 131 | break; 132 | } 133 | }, 134 | 135 | 136 | /** 137 | * Событие переноса файла через dropzone 138 | * @param {Event} event 139 | */ 140 | onDragOver: function(event) { 141 | 142 | event.stopPropagation(); 143 | event.preventDefault(); 144 | event.dataTransfer.dropEffect = 'copy'; 145 | 146 | let dropzone = this.getDropzone(); 147 | 148 | if (dropzone) { 149 | dropzone.addClass('over'); 150 | } 151 | }, 152 | 153 | 154 | /** 155 | * Событие завершения перетаскивания с отпускаем кнопки мыши 156 | * @param {Event} event 157 | */ 158 | onDragLeave: function(event) { 159 | 160 | let dropzone = this.getDropzone(); 161 | if (dropzone) { 162 | dropzone.removeClass('over'); 163 | } 164 | }, 165 | 166 | 167 | /** 168 | * Событие когда перетаскиваемый элемент или выделенный текст покидают допустимую цель перетаскивания 169 | * @param {Event} event 170 | */ 171 | onDragEnd: function(event) { 172 | 173 | let dropzone = this.getDropzone(); 174 | if (dropzone) { 175 | dropzone.removeClass('over'); 176 | } 177 | }, 178 | 179 | 180 | /** 181 | * Событие переноса файла в dropzone 182 | * @param {Event} event 183 | */ 184 | onDragEnter: function(event) { 185 | 186 | event.stopPropagation(); 187 | event.preventDefault(); 188 | event.dataTransfer.dropEffect = 'copy'; 189 | } 190 | } 191 | 192 | export default fileUpEvents; -------------------------------------------------------------------------------- /src/js/fileup.file.js: -------------------------------------------------------------------------------- 1 | import fileUpPrivate from "./fileup.private"; 2 | import fileUpUtils from "./fileup.utils"; 3 | 4 | let fileUpFile = { 5 | 6 | _options: { 7 | name: null, 8 | size: null, 9 | urlPreview: null, 10 | urlDownload: null 11 | }, 12 | 13 | _id: '', 14 | _status: 'stand_by', 15 | _fileElement: null, 16 | _file: null, 17 | _fileUp: null, 18 | _xhr: null, 19 | 20 | 21 | /** 22 | * Инициализация 23 | * @param {object} fileUp 24 | * @param {int} id 25 | * @param {object} options 26 | * @param {File} file 27 | * @private 28 | */ 29 | _init: function (fileUp, id, options, file) { 30 | 31 | if ( ! fileUpUtils.isObject(options)) { 32 | throw new Error('File incorrect options param'); 33 | } 34 | 35 | if (typeof id !== 'number' || id < 0) { 36 | throw new Error('File dont set or incorrect id param'); 37 | } 38 | if (typeof options.name !== 'string' || ! options.name) { 39 | throw new Error('File dont set name param'); 40 | } 41 | 42 | this._fileUp = fileUp; 43 | this._options = $.extend(true, {}, this._options, options); 44 | this._id = id; 45 | 46 | 47 | if (file instanceof File) { 48 | let xhr = null; 49 | 50 | if (window.XMLHttpRequest) { 51 | xhr = ("onload" in new XMLHttpRequest()) ? new XMLHttpRequest : new XDomainRequest; 52 | 53 | } else if (window.ActiveXObject) { 54 | try { 55 | xhr = new ActiveXObject("Msxml2.XMLHTTP"); 56 | } catch (e) { 57 | try { 58 | xhr = new ActiveXObject("Microsoft.XMLHTTP"); 59 | } catch (e) { 60 | fileUpPrivate.trigger(fileUp, 'error', ['old_browser', { file: this }]); 61 | } 62 | } 63 | } else { 64 | fileUpPrivate.trigger(fileUp, 'error', ['old_browser', { file: this }]); 65 | } 66 | 67 | if ( ! xhr) { 68 | throw new Error('xhr dont created. Check your browser'); 69 | } 70 | 71 | this._xhr = xhr; 72 | this._file = file; 73 | 74 | } else { 75 | this._status = 'finish'; 76 | } 77 | }, 78 | 79 | 80 | /** 81 | * Получение id файла 82 | * @return {null} 83 | */ 84 | getId: function () { 85 | return this._id; 86 | }, 87 | 88 | 89 | /** 90 | * Получение name 91 | * @return {string|null} 92 | */ 93 | getName: function () { 94 | 95 | return this._file 96 | ? fileUpUtils.getFileName(this._file) 97 | : this._options.name; 98 | }, 99 | 100 | 101 | /** 102 | * Получение элемента файла 103 | * @return {jQuery|null} 104 | */ 105 | getElement: function () { 106 | return this._fileElement; 107 | }, 108 | 109 | 110 | /** 111 | * Получение urlPreview 112 | * @return {string|null} 113 | */ 114 | getUrlPreview: function () { 115 | return this._options.urlPreview; 116 | }, 117 | 118 | 119 | /** 120 | * Получение urlDownload 121 | * @return {string|null} 122 | */ 123 | getUrlDownload: function () { 124 | return this._options.urlDownload; 125 | }, 126 | 127 | 128 | /** 129 | * Получение size 130 | * @return {int|null} 131 | */ 132 | getSize: function () { 133 | 134 | return this._file 135 | ? fileUpUtils.getFileSize(this._file) 136 | : this._options.size; 137 | }, 138 | 139 | 140 | /** 141 | * Formatting size 142 | * @returns {string} 143 | */ 144 | getSizeHuman: function() { 145 | 146 | let size = this.getSize(); 147 | 148 | return fileUpUtils.getSizeHuman(size); 149 | }, 150 | 151 | 152 | /** 153 | * Получение xhr 154 | * @return {XMLHttpRequest|null} 155 | */ 156 | getXhr: function () { 157 | return this._xhr; 158 | }, 159 | 160 | 161 | /** 162 | * Получение файла 163 | * @return {File|null} 164 | */ 165 | getFile: function () { 166 | 167 | if ( ! (this._file instanceof File)) { 168 | return null; 169 | } 170 | 171 | return this._file; 172 | }, 173 | 174 | 175 | /** 176 | * Получение статуса 177 | * @return {string} 178 | */ 179 | getStatus: function () { 180 | return this._status; 181 | }, 182 | 183 | 184 | 185 | /** 186 | * Установка статуса 187 | * @param {string} status 188 | */ 189 | setStatus: function (status) { 190 | 191 | if (typeof status !== 'string') { 192 | return; 193 | } 194 | 195 | this._status = status; 196 | }, 197 | 198 | 199 | /** 200 | * Получение параметров 201 | * 202 | * @returns {object} 203 | */ 204 | getOptions: function () { 205 | return this._options; 206 | }, 207 | 208 | 209 | /** 210 | * Получение параметра 211 | * @param {string} name 212 | * @returns {*} 213 | */ 214 | getOption: function (name) { 215 | 216 | if (typeof name !== 'string' || ! this._options.hasOwnProperty(name)) { 217 | return null; 218 | } 219 | 220 | return this._options[name]; 221 | }, 222 | 223 | 224 | /** 225 | * Установка параметра 226 | * @param {string} name 227 | * @param {*} value 228 | */ 229 | setOption: function (name, value) { 230 | 231 | if (typeof name !== 'string') { 232 | return; 233 | } 234 | 235 | this._options[name] = value; 236 | }, 237 | 238 | 239 | /** 240 | * Показ сообщения об ошибке 241 | * @param {string} message 242 | */ 243 | showError: function (message) { 244 | 245 | if (typeof message !== 'string') { 246 | return; 247 | } 248 | 249 | let element = this.getElement(); 250 | 251 | if (element) { 252 | element.find('.fileup-result') 253 | .removeClass('fileup-success') 254 | .addClass('fileup-error') 255 | .text(message); 256 | } 257 | }, 258 | 259 | 260 | /** 261 | * Показ сообщения об успехе 262 | * @param {string} message 263 | */ 264 | showSuccess: function (message) { 265 | 266 | if (typeof message !== 'string') { 267 | return; 268 | } 269 | 270 | let element = this.getElement(); 271 | 272 | if (element) { 273 | element.find('.fileup-result') 274 | .removeClass('fileup-error') 275 | .addClass('fileup-success') 276 | .text(message); 277 | } 278 | }, 279 | 280 | 281 | /** 282 | * Удаление файла на странице и из памяти 283 | */ 284 | remove: function () { 285 | 286 | this.abort(); 287 | 288 | if (this._fileElement) { 289 | this._fileElement.fadeOut('fast', function () { 290 | this.remove(); 291 | }); 292 | } 293 | 294 | let fileId = this.getId(); 295 | 296 | if (this._fileUp._files.hasOwnProperty(fileId)) { 297 | delete this._fileUp._files[fileId]; 298 | } 299 | 300 | fileUpPrivate.trigger(this._fileUp, 'remove', [this]); 301 | }, 302 | 303 | 304 | /** 305 | * Загрузка файла 306 | * @return {boolean} 307 | */ 308 | upload: function() { 309 | 310 | let file = this.getFile(); 311 | let xhr = this.getXhr(); 312 | 313 | if ( ! file || ! xhr) { 314 | return false; 315 | } 316 | 317 | 318 | let options = this._fileUp.getOptions(); 319 | let that = this; 320 | 321 | if (typeof options.timeout === 'number') { 322 | xhr.timeout = options.timeout; 323 | } 324 | 325 | // запрос начат 326 | xhr.onloadstart = function() { 327 | that.setStatus('load_start'); 328 | fileUpPrivate.trigger(that._fileUp, 'load_start', [that]); 329 | }; 330 | 331 | // браузер получил очередной пакет данных 332 | xhr.upload.onprogress = function(ProgressEvent) { 333 | fileUpPrivate.trigger(that._fileUp, 'load_progress', [that, ProgressEvent]); 334 | }; 335 | 336 | // запрос был успешно (без ошибок) завершён 337 | xhr.onload = function() { 338 | that.setStatus('loaded'); 339 | 340 | if (xhr.status === 200) { 341 | fileUpPrivate.trigger(that._fileUp, 'load_success', [that, xhr.responseText]); 342 | } else { 343 | fileUpPrivate.trigger(that._fileUp, 'error', [ 344 | 'load_bad_status', 345 | { 346 | file: that, 347 | fileData: file, 348 | response: xhr.responseText, 349 | xhr: xhr, 350 | } 351 | ]); 352 | } 353 | }; 354 | 355 | // запрос был завершён (успешно или неуспешно) 356 | xhr.onloadend = function() { 357 | that.setStatus('finish'); 358 | fileUpPrivate.trigger(that._fileUp, 'load_finish', [that]); 359 | }; 360 | 361 | // запрос был отменён вызовом xhr.abort() 362 | xhr.onabort = function() { 363 | that.setStatus('stand_by'); 364 | fileUpPrivate.trigger(that._fileUp, 'load_abort', [that]); 365 | }; 366 | 367 | // запрос был прекращён по таймауту 368 | xhr.ontimeout = function() { 369 | that.setStatus('stand_by'); 370 | fileUpPrivate.trigger(that._fileUp, 'error', [ 371 | 'load_timeout', 372 | { 373 | file: that, 374 | fileData: file 375 | } 376 | ]); 377 | }; 378 | 379 | // произошла ошибка 380 | xhr.onerror = function(event) { 381 | that.setStatus('stand_by'); 382 | fileUpPrivate.trigger(that._fileUp, 'error', [ 383 | 'load_error', 384 | { 385 | file: that, 386 | fileData: file, 387 | event: event 388 | } 389 | ]); 390 | }; 391 | 392 | xhr.open(options.httpMethod || 'post', options.url, true); 393 | xhr.setRequestHeader('Cache-Control', 'no-cache'); 394 | xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 395 | 396 | 397 | fileUpPrivate.trigger(that._fileUp, 'load_before_start', [that, xhr]); 398 | 399 | if (window.FormData !== undefined) { 400 | let formData = new FormData(); 401 | formData.append(options.fieldName, file); 402 | 403 | if (Object.keys(options.extraFields).length) { 404 | $.each(options.extraFields, function(name, value){ 405 | formData.append(name, value); 406 | }); 407 | } 408 | 409 | return xhr.send(formData); 410 | 411 | } else { 412 | // IE 8,9 413 | return xhr.send(file); 414 | } 415 | }, 416 | 417 | 418 | /** 419 | * Отмена загрузки 420 | */ 421 | abort: function() { 422 | 423 | if (this._xhr) { 424 | this._xhr.abort(); 425 | } 426 | }, 427 | 428 | 429 | /** 430 | * Рендер элемента 431 | * @param {string} tpl 432 | * @return {string|null} 433 | */ 434 | render: function (tpl) { 435 | 436 | if ( ! tpl || typeof tpl !== 'string') { 437 | return null; 438 | } 439 | 440 | let lang = this._fileUp.getLang(); 441 | let options = this._fileUp.getOptions(); 442 | let that = this; 443 | let isNoPreview = false; 444 | let mimeTypes = fileUpUtils.isObject(options.mimeTypes) ? options.mimeTypes : {}; 445 | let iconDefault = typeof options.iconDefault === 'string' ? options.iconDefault : ''; 446 | let showRemove = typeof options.showRemove === 'boolean' ? options.showRemove : true; 447 | let size = this.getSizeHuman(); 448 | let icon = null; 449 | 450 | let fileType = null; 451 | let fileExt = null; 452 | 453 | tpl = tpl.replace(/\[NAME\]/g, this.getName()); 454 | tpl = tpl.replace(/\[SIZE\]/g, size); 455 | tpl = tpl.replace(/\[UPLOAD\]/g, lang.upload); 456 | tpl = tpl.replace(/\[REMOVE\]/g, lang.remove); 457 | tpl = tpl.replace(/\[ABORT\]/g, lang.abort); 458 | 459 | if (this._file && 460 | this._file instanceof File 461 | ) { 462 | if (this._file.type && 463 | typeof this._file.type === 'string' && 464 | this._file.type.match(/^image\/.*/) 465 | ) { 466 | if (typeof FileReader !== 'undefined') { 467 | let reader = new FileReader(); 468 | reader.onload = function (ProgressEvent) { 469 | if (that._fileElement) { 470 | let preview = that._fileElement.find('.fileup-preview'); 471 | 472 | preview.removeClass('no-preview') 473 | .find('img').attr('src', ProgressEvent.target.result) 474 | } 475 | }; 476 | reader.readAsDataURL(this._file); 477 | } 478 | 479 | isNoPreview = true; 480 | 481 | tpl = tpl.replace(/\[PREVIEW_SRC\]/g, ''); 482 | tpl = tpl.replace(/\[TYPE\]/g, 'fileup-image fileup-no-preview'); 483 | 484 | } else { 485 | tpl = tpl.replace(/\[PREVIEW_SRC\]/g, ''); 486 | tpl = tpl.replace(/\[TYPE\]/g, 'fileup-doc'); 487 | 488 | fileType = this._file.type; 489 | fileExt = this.getName().split('.').pop(); 490 | } 491 | 492 | } else { 493 | let urlPreview = this.getUrlPreview(); 494 | 495 | tpl = tpl.replace(/\[PREVIEW_SRC\]/g, urlPreview ? urlPreview : ''); 496 | tpl = tpl.replace(/\[TYPE\]/g, urlPreview ? 'fileup-image' : 'fileup-doc'); 497 | 498 | fileExt = this.getName() ? this.getName().split('.').pop().toLowerCase() : ''; 499 | } 500 | 501 | 502 | this._fileElement = $(tpl); 503 | 504 | if (isNoPreview) { 505 | this._fileElement.find('.fileup-preview').addClass('no-preview'); 506 | } 507 | if ( ! size) { 508 | this._fileElement.find('.fileup-size').hide(); 509 | } 510 | 511 | if (fileType || fileExt) { 512 | $.each(mimeTypes, function (name, type) { 513 | if ( ! fileUpUtils.isObject(type) || 514 | ! type.hasOwnProperty('icon') || 515 | typeof type.icon !== 'string' || 516 | type.icon === '' 517 | ) { 518 | return; 519 | } 520 | 521 | if (fileType && type.hasOwnProperty('mime')) { 522 | if (typeof type.mime === 'string') { 523 | if (type.mime === fileType) { 524 | icon = type.icon; 525 | return false; 526 | } 527 | 528 | } else if (Array.isArray(type.mime)) { 529 | $.each(type.mime, function (key, mime) { 530 | if (typeof mime === 'string' && mime === fileType) { 531 | icon = type.icon; 532 | return false; 533 | } 534 | }); 535 | 536 | if (icon) { 537 | return false; 538 | } 539 | 540 | } else if (type.mime instanceof RegExp) { 541 | if (type.mime.test(fileType)) { 542 | icon = type.icon; 543 | return false; 544 | } 545 | } 546 | } 547 | 548 | if (fileExt && type.hasOwnProperty('ext') && Array.isArray(type.ext)) { 549 | $.each(type.ext, function (key, ext) { 550 | if (typeof ext === 'string' && ext === fileExt) { 551 | icon = type.icon; 552 | return false; 553 | } 554 | }); 555 | 556 | if (icon) { 557 | return false; 558 | } 559 | } 560 | }); 561 | } 562 | 563 | if ( ! icon) { 564 | icon = iconDefault; 565 | } 566 | 567 | this._fileElement.find('.fileup-icon').addClass(icon); 568 | 569 | 570 | if ( ! showRemove) { 571 | this._fileElement.find('.fileup-remove').hide(); 572 | } 573 | 574 | if (this.getUrlDownload()) { 575 | let $name = this._fileElement.find('.fileup-name'); 576 | if ($name[0]) { 577 | $name.replaceWith( 578 | '' + 579 | this.getName() + 580 | '' 581 | ); 582 | } 583 | } 584 | 585 | if (this._status === 'finish') { 586 | this._fileElement.find('.fileup-upload').hide(); 587 | this._fileElement.find('.fileup-abort').hide(); 588 | this._fileElement.find('.fileup-progress').hide(); 589 | 590 | } else { 591 | this._fileElement.find('.fileup-upload').click(function () { 592 | that.upload(); 593 | }); 594 | 595 | this._fileElement.find('.fileup-abort').click(function () { 596 | that.abort(); 597 | }); 598 | } 599 | 600 | this._fileElement.find('.fileup-remove').click(function () { 601 | that.remove(); 602 | }); 603 | 604 | 605 | return this._fileElement; 606 | } 607 | } 608 | 609 | export default fileUpFile; -------------------------------------------------------------------------------- /src/js/fileup.instance.js: -------------------------------------------------------------------------------- 1 | import fileUpUtils from './fileup.utils'; 2 | import fileUpPrivate from './fileup.private'; 3 | import fileUpFile from "./fileup.file"; 4 | import tpl from './fileup.templates'; 5 | 6 | let fileUpInstance = { 7 | 8 | _options: { 9 | id: null, 10 | url: null, 11 | input: null, 12 | queue: null, 13 | dropzone: null, 14 | files: [], 15 | fieldName: 'file', 16 | extraFields: {}, 17 | lang: 'en', 18 | langItems: null, 19 | sizeLimit: 0, 20 | filesLimit: 0, 21 | httpMethod: 'post', 22 | timeout: null, 23 | autostart: false, 24 | showRemove: true, 25 | templateFile: null, 26 | 27 | onSelect: null, 28 | onRemove: null, 29 | onBeforeStart: null, 30 | onStart: null, 31 | onProgress: null, 32 | onAbort: null, 33 | onSuccess: null, 34 | onFinish: null, 35 | onError: null, 36 | onDragOver: null, 37 | onDragLeave: null, 38 | onDragEnd: null, 39 | onDragEnter: null, 40 | 41 | iconDefault: 'bi bi-file-earmark-text', 42 | mimeTypes: { 43 | archive: { 44 | mime: ['application/zip', 'application/gzip', 'application/x-bzip', 'application/x-bzip2', 'application/x-7z-compressed'], 45 | ext: ['zip', '7z', 'bz', 'bz2', 'gz', 'jar', 'rar', 'tar'], 46 | icon: 'bi bi-file-earmark-zip' 47 | }, 48 | word: { 49 | mime: ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 50 | ext: ['doc', 'docx'], 51 | icon: 'bi bi-file-earmark-word' 52 | }, 53 | excel: { 54 | mime: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], 55 | ext: ['xls', 'xlsx'], 56 | icon: 'bi bi-file-earmark-excel' 57 | }, 58 | image: { 59 | mime: /image\/.*/, 60 | ext: ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'raw', 'webp', 'heic', 'ico'], 61 | icon: 'bi bi-file-earmark-image' 62 | }, 63 | video: { 64 | mime: /video\/.*/, 65 | ext: ['avi', 'mp4', 'mpeg', 'ogv', 'ts', 'webm', '3gp', '3g2', 'mkv'], 66 | icon: 'bi bi-file-earmark-play' 67 | }, 68 | audio: { 69 | mime: /audio\/.*/, 70 | ext: ['avi', 'mp4', 'mpeg', 'ogv', 'ts', 'webm', '3gp', '3g2', 'mkv'], 71 | icon: 'bi bi-file-earmark-music' 72 | }, 73 | pdf: { 74 | mime: ['application/pdf'], 75 | ext: ['pdf'], 76 | icon: 'bi bi-file-earmark-pdf' 77 | }, 78 | binary: { 79 | mime: ['application\/octet-stream'], 80 | ext: ['bin', 'exe', 'dat', 'dll'], 81 | icon: 'bi bi-file-earmark-binary' 82 | } 83 | } 84 | }, 85 | 86 | _id: null, 87 | _fileUp: null, 88 | _fileIndex: 0, 89 | _input: null, 90 | _queue: null, 91 | _dropzone: null, 92 | _files: {}, 93 | _events: {}, 94 | 95 | 96 | /** 97 | * Инициализация 98 | * @param {object} fileUp 99 | * @param {object} options 100 | * @private 101 | */ 102 | _init: function (fileUp, options) { 103 | 104 | if (typeof options.url !== 'string' || ! options.url) { 105 | throw new Error('Dont set url param'); 106 | } 107 | 108 | this._fileUp = fileUp; 109 | this._options = $.extend(true, {}, this._options, options); 110 | this._id = typeof this._options.id === 'string' && this._options.id 111 | ? this._options.id 112 | : fileUpUtils.hashCode(); 113 | 114 | if ( ! this._options.templateFile || typeof this._options.templateFile !== 'string') { 115 | this._options.templateFile = tpl['file.html']; 116 | } 117 | 118 | 119 | fileUpPrivate.initInput(this); 120 | fileUpPrivate.initQueue(this); 121 | fileUpPrivate.initDropzone(this); 122 | fileUpPrivate.initEvents(this); 123 | 124 | fileUpPrivate.renderFiles(this); 125 | }, 126 | 127 | 128 | /** 129 | * Разрушение экземпляра 130 | */ 131 | destruct: function () { 132 | 133 | let id = this.getId(); 134 | 135 | if ( ! this._fileUp._instances.hasOwnProperty(id)) { 136 | return; 137 | } 138 | 139 | delete this._fileUp._instances[id]; 140 | }, 141 | 142 | 143 | /** 144 | * Получение параметров 145 | * @returns {object} 146 | */ 147 | getOptions: function () { 148 | return this._options; 149 | }, 150 | 151 | 152 | /** 153 | * Получение id 154 | * @return {string|null} 155 | */ 156 | getId: function () { 157 | return this._id; 158 | }, 159 | 160 | 161 | /** 162 | * Получение input 163 | * @return {jQuery|null} 164 | */ 165 | getInput: function () { 166 | return this._input; 167 | }, 168 | 169 | 170 | /** 171 | * Получение queue 172 | * @return {jQuery|null} 173 | */ 174 | getQueue: function () { 175 | return this._queue; 176 | }, 177 | 178 | 179 | /** 180 | * Получение dropzone 181 | * @return {jQuery|null} 182 | */ 183 | getDropzone: function () { 184 | return this._dropzone; 185 | }, 186 | 187 | 188 | /** 189 | * Подписка на событие 190 | * @param {string} eventName 191 | * @param {function|string} callback 192 | * @param {object|undefined} context 193 | */ 194 | on: function(eventName, callback, context) { 195 | if (typeof this._events[eventName] !== 'object') { 196 | this._events[eventName] = []; 197 | } 198 | this._events[eventName].push({ 199 | context : context || this, 200 | callback: callback, 201 | singleExec: false 202 | }); 203 | }, 204 | 205 | 206 | /** 207 | * Подписка на событие таким образом, что выполнение произойдет лишь один раз 208 | * @param {string} eventName 209 | * @param {function|string} callback 210 | * @param {object|undefined} context 211 | */ 212 | one: function(eventName, callback, context) { 213 | if (typeof this._events[eventName] !== 'object') { 214 | this._events[eventName] = []; 215 | } 216 | this._events[eventName].push({ 217 | context : context || this, 218 | callback: callback, 219 | singleExec: true, 220 | }); 221 | }, 222 | 223 | 224 | /** 225 | * Получение настроек языка 226 | */ 227 | getLang: function () { 228 | return $.extend(true, {}, this._options.langItems); 229 | }, 230 | 231 | 232 | /** 233 | * Получение всех файлов 234 | * @return {object} 235 | */ 236 | getFiles: function () { 237 | 238 | return this._files; 239 | }, 240 | 241 | 242 | /** 243 | * Получение файла по его id 244 | * @param {int} fileId 245 | * @return {object|null} 246 | */ 247 | getFileById: function (fileId) { 248 | 249 | let result = null; 250 | 251 | $.each(this._files, function (key, file) { 252 | if (fileId === file.getId()) { 253 | result = file; 254 | } 255 | }); 256 | 257 | return result; 258 | }, 259 | 260 | 261 | /** 262 | * Удаление всех файлов 263 | */ 264 | removeAll: function() { 265 | 266 | $.each(this._files, function (key, file) { 267 | file.remove(); 268 | }); 269 | }, 270 | 271 | 272 | /** 273 | * Загрузка всех файлов 274 | */ 275 | uploadAll: function() { 276 | 277 | $.each(this._files, function (key, file) { 278 | file.upload(); 279 | }); 280 | }, 281 | 282 | 283 | /** 284 | * Отмена загрузки всех файлов 285 | */ 286 | abortAll: function() { 287 | 288 | $.each(this._files, function (key, file) { 289 | file.abort(); 290 | }); 291 | }, 292 | 293 | 294 | /** 295 | * Добавление файла в список из объекта File 296 | * @param {object} file 297 | * @result {boolean} 298 | */ 299 | appendFile: function (file) { 300 | 301 | if ( ! (file instanceof File)) { 302 | return false; 303 | } 304 | 305 | let fileInstance = $.extend(true, {}, fileUpFile); 306 | let data = { 307 | name: fileUpUtils.getFileName(file), 308 | size: fileUpUtils.getFileSize(file), 309 | type: file.type 310 | }; 311 | 312 | 313 | fileInstance._init(this, this._fileIndex, data, file); 314 | 315 | this._files[this._fileIndex] = fileInstance; 316 | 317 | let queue = this.getQueue(); 318 | if (queue) { 319 | queue.append( 320 | fileInstance.render(this._options.templateFile) 321 | ); 322 | } 323 | 324 | this._fileIndex++; 325 | 326 | 327 | if (typeof this._options.autostart === 'boolean' && 328 | this._options.autostart 329 | ) { 330 | fileInstance.upload(); 331 | } 332 | 333 | return true; 334 | }, 335 | 336 | 337 | /** 338 | * Добавление файла в список из данных 339 | * @param {object} data 340 | * @result {boolean} 341 | */ 342 | appendFileByData: function (data) { 343 | 344 | if ( ! fileUpUtils.isObject(data)) { 345 | return false; 346 | } 347 | 348 | let fileInstance = $.extend(true, {}, fileUpFile); 349 | 350 | fileInstance._init(this, this._fileIndex, data); 351 | fileInstance.setStatus('finish'); 352 | 353 | this._files[this._fileIndex] = fileInstance; 354 | 355 | let queue = this.getQueue(); 356 | if (queue) { 357 | queue.append( 358 | fileInstance.render(this._options.templateFile) 359 | ); 360 | } 361 | 362 | this._fileIndex++; 363 | 364 | return true; 365 | } 366 | } 367 | 368 | export default fileUpInstance; -------------------------------------------------------------------------------- /src/js/fileup.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUpInstance from './fileup.instance'; 3 | import fileUpUtils from './fileup.utils'; 4 | 5 | let fileUp = { 6 | 7 | lang: {}, 8 | 9 | _instances: {}, 10 | 11 | /** 12 | * Создание экземпляра 13 | * @param {object} options 14 | * @returns {object} 15 | */ 16 | create: function (options) { 17 | 18 | options = fileUpUtils.isObject(options) ? options : {}; 19 | 20 | if ( ! options.hasOwnProperty('lang')) { 21 | options.lang = 'en'; 22 | } 23 | 24 | let langList = this.lang.hasOwnProperty(options.lang) ? this.lang[options.lang] : {}; 25 | options.langItems = options.hasOwnProperty('langItems') && fileUpUtils.isObject(options.langItems) 26 | ? $.extend(true, {}, langList, options.langItems) 27 | : langList; 28 | 29 | let instance = $.extend(true, {}, fileUpInstance); 30 | instance._init(this, options); 31 | 32 | let id = instance.getId(); 33 | this._instances[id] = instance; 34 | 35 | return instance; 36 | }, 37 | 38 | 39 | /** 40 | * Получение экземпляра по id 41 | * @param {string} id 42 | * @returns {object|null} 43 | */ 44 | get: function (id) { 45 | 46 | if ( ! this._instances.hasOwnProperty(id)) { 47 | return null; 48 | } 49 | 50 | if ( ! $.contains(document, this._instances[id]._input[0])) { 51 | delete this._instances[id]; 52 | return null; 53 | } 54 | 55 | return this._instances[id]; 56 | } 57 | } 58 | 59 | export default fileUp; -------------------------------------------------------------------------------- /src/js/fileup.private.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUpUtils from "./fileup.utils"; 3 | import fileUpEvents from "./fileup.events"; 4 | 5 | 6 | let fileUpPrivate = { 7 | 8 | /** 9 | * 10 | * @param {object} fileUp 11 | */ 12 | initInput: function (fileUp) { 13 | 14 | let input = null; 15 | 16 | if (fileUp._options.input instanceof HTMLElement || 17 | fileUp._options.input instanceof jQuery 18 | ) { 19 | input = $(fileUp._options.input); 20 | 21 | } else if (typeof fileUp._options.input === 'string' && 22 | fileUp._options.input 23 | ) { 24 | input = $('#' + fileUp._options.input); 25 | } 26 | 27 | if ( ! input || ! $(input)[0] || $(input)[0].type !== 'file') { 28 | throw new Error('Not found input element'); 29 | } 30 | 31 | fileUp._input = input; 32 | }, 33 | 34 | 35 | /** 36 | * 37 | * @param {object} fileUp 38 | */ 39 | initQueue: function (fileUp) { 40 | 41 | let queue = null; 42 | 43 | if (fileUp._options.queue instanceof HTMLElement || 44 | fileUp._options.queue instanceof jQuery 45 | ) { 46 | queue = $(fileUp._options.queue); 47 | 48 | } else if (typeof fileUp._options.queue === 'string' && 49 | fileUp._options.queue 50 | ) { 51 | queue = $('#' + fileUp._options.queue); 52 | } 53 | 54 | if ( ! queue || ! $(queue)[0]) { 55 | throw new Error('Not found queue element'); 56 | } 57 | 58 | fileUp._queue = queue; 59 | }, 60 | 61 | 62 | /** 63 | * 64 | * @param {object} fileUp 65 | */ 66 | initDropzone: function (fileUp) { 67 | 68 | let dropzone = null; 69 | 70 | if (fileUp._options.dropzone instanceof HTMLElement || 71 | fileUp._options.dropzone instanceof jQuery 72 | ) { 73 | dropzone = $(fileUp._options.dropzone); 74 | 75 | } else if (typeof fileUp._options.dropzone === 'string' && 76 | fileUp._options.dropzone 77 | ) { 78 | dropzone = $('#' + fileUp._options.dropzone); 79 | } 80 | 81 | 82 | if (dropzone) { 83 | fileUp._dropzone = dropzone; 84 | 85 | let that = this; 86 | 87 | dropzone.on('click', function () { 88 | fileUp.getInput().click(); 89 | }); 90 | 91 | dropzone[0].addEventListener('dragover', function (event) { 92 | that.trigger(fileUp, 'drag_over', [event]); 93 | }); 94 | 95 | dropzone[0].addEventListener('dragleave', function (event) { 96 | that.trigger(fileUp, 'drag_leave', [event]); 97 | }); 98 | 99 | dropzone[0].addEventListener('dragenter', function (event) { 100 | that.trigger(fileUp, 'drag_enter', [event]); 101 | }); 102 | 103 | dropzone[0].addEventListener('dragend', function (event) { 104 | that.trigger(fileUp, 'drag_end', [event]); 105 | }); 106 | 107 | dropzone[0].addEventListener('drop', function (event) { 108 | fileUp.getInput()[0].files = event.target.files || event.dataTransfer.files 109 | that.appendFiles(fileUp, event) 110 | }); 111 | } 112 | }, 113 | 114 | 115 | /** 116 | * Инициализация событий 117 | * @param {object} fileUp 118 | */ 119 | initEvents: function (fileUp) { 120 | 121 | /** 122 | * @param {string} name 123 | * @param {function|string} func 124 | */ 125 | function setEvent(name, func) { 126 | 127 | let event = null; 128 | 129 | if (typeof func === 'function') { 130 | event = func; 131 | } else if (typeof func === 'string') { 132 | event = new Function(func); 133 | } 134 | 135 | if (event) { 136 | fileUp.on(name, event); 137 | } 138 | } 139 | 140 | 141 | let options = fileUp.getOptions(); 142 | let that = this; 143 | 144 | setEvent('load_start', fileUpEvents.onLoadStart); 145 | setEvent('load_progress', fileUpEvents.onLoadProgress); 146 | setEvent('load_abort', fileUpEvents.onLoadAbort); 147 | setEvent('load_success', fileUpEvents.onSuccess); 148 | setEvent('error', fileUpEvents.onError); 149 | setEvent('drag_over', fileUpEvents.onDragOver); 150 | setEvent('drag_leave', fileUpEvents.onDragEnter); 151 | setEvent('drag_end', fileUpEvents.onDragLeave); 152 | setEvent('drag_enter', fileUpEvents.onDragEnd); 153 | 154 | if (options.onSelect) { setEvent('select', options.onSelect) } 155 | if (options.onRemove) { setEvent('remove', options.onRemove) } 156 | if (options.onBeforeStart) { setEvent('load_before_start', options.onBeforeStart) } 157 | if (options.onStart) { setEvent('load_start', options.onStart) } 158 | if (options.onProgress) { setEvent('load_progress', options.onProgress) } 159 | if (options.onAbort) { setEvent('load_abort', options.onAbort) } 160 | if (options.onSuccess) { setEvent('load_success', options.onSuccess) } 161 | if (options.onFinish) { setEvent('load_finish', options.onFinish) } 162 | if (options.onError) { setEvent('error', options.onError) } 163 | if (options.onDragOver) { setEvent('drag_over', options.onDragOver) } 164 | if (options.onDragLeave) { setEvent('drag_leave', options.onDragLeave) } 165 | if (options.onDragEnd) { setEvent('drag_end', options.onDragEnd) } 166 | if (options.onDragEnter) { setEvent('drag_enter', options.onDragEnter) } 167 | 168 | 169 | fileUp.getInput().on('change', function (event) { 170 | that.appendFiles(fileUp, event); 171 | }); 172 | }, 173 | 174 | 175 | /** 176 | * Формирование списка ранее загруженных файлов 177 | * @param {object} fileUp 178 | */ 179 | renderFiles: function (fileUp) { 180 | 181 | let options = fileUp.getOptions(); 182 | 183 | if (Array.isArray(options.files) && options.files.length > 0) { 184 | for (var i = 0; i < options.files.length; i++) { 185 | if ( ! fileUpUtils.isObject(options.files[i])) { 186 | continue; 187 | } 188 | 189 | fileUp.appendFileByData(options.files[i]); 190 | } 191 | } 192 | }, 193 | 194 | 195 | /** 196 | * @param fileUp 197 | * @param name 198 | * @param params 199 | * @return {object} 200 | * @private 201 | */ 202 | trigger: function(fileUp, name, params) { 203 | 204 | params = params || []; 205 | let results = []; 206 | 207 | if (fileUp._events[name] instanceof Object && fileUp._events[name].length > 0) { 208 | for (var i = 0; i < fileUp._events[name].length; i++) { 209 | let callback = fileUp._events[name][i].callback; 210 | 211 | results.push( 212 | callback.apply(fileUp._events[name][i].context || fileUp, params) 213 | ); 214 | 215 | if (fileUp._events[name][i].singleExec) { 216 | fileUp._events[name].splice(i, 1); 217 | i--; 218 | } 219 | } 220 | } 221 | 222 | return results; 223 | }, 224 | 225 | 226 | /** 227 | * Append files in queue 228 | * @param {object} fileUp 229 | * @param {Event} event 230 | */ 231 | appendFiles: function(fileUp, event) { 232 | 233 | event.preventDefault(); 234 | event.stopPropagation(); 235 | 236 | let options = fileUp.getOptions(); 237 | let input = fileUp.getInput(); 238 | let files = input[0].files; 239 | let multiple = input.is("[multiple]"); 240 | 241 | if (files.length > 0) { 242 | for (var i = 0; i < files.length; i++) { 243 | let file = files[i]; 244 | 245 | if (options.sizeLimit > 0 && fileUpUtils.getFileSize(file) > options.sizeLimit) { 246 | this.trigger(fileUp, 'error', ['size_limit', { fileData: file, sizeLimit: options.sizeLimit }]); 247 | continue; 248 | } 249 | 250 | if (options.filesLimit > 0 && Object.keys(fileUp._files).length >= options.filesLimit) { 251 | this.trigger(fileUp, 'error', ['files_limit', { fileData: file, filesLimit: options.filesLimit }]); 252 | break; 253 | } 254 | 255 | if (typeof input[0].accept === 'string') { 256 | let accept = input[0].accept; 257 | if (accept && /[^\w]+/.test(accept)) { 258 | let isAccept = false; 259 | let types = accept.split(','); 260 | 261 | if (types.length > 0) { 262 | for (var t = 0; t < types.length; t++) { 263 | types[t] = types[t].replace(/\s/g, ''); 264 | if (new RegExp(types[t].replace('*', '.*')).test(file.type) || 265 | new RegExp(types[t].replace('.', '.*/')).test(file.type) 266 | ) { 267 | isAccept = true; 268 | break; 269 | } 270 | } 271 | } 272 | if ( ! isAccept) { 273 | this.trigger(fileUp, 'error', ['file_type', { fileData: file }]); 274 | continue; 275 | } 276 | } 277 | } 278 | 279 | let results = this.trigger(fileUp, 'select', [file]); 280 | 281 | if (results) { 282 | let isContinue = false; 283 | 284 | $.each(results, function (key, result) { 285 | if (result === false) { 286 | isContinue = true; 287 | return false; 288 | } 289 | }) 290 | 291 | if (isContinue) { 292 | continue; 293 | } 294 | } 295 | 296 | if ( ! multiple) { 297 | fileUp.removeAll(); 298 | } 299 | 300 | fileUp.appendFile(file); 301 | 302 | if ( ! multiple) { 303 | break; 304 | } 305 | } 306 | 307 | input.val(''); 308 | } 309 | 310 | this.trigger(fileUp, 'dragEnd', [event]); 311 | } 312 | } 313 | 314 | export default fileUpPrivate; -------------------------------------------------------------------------------- /src/js/fileup.templates.js: -------------------------------------------------------------------------------- 1 | let tpl = Object.create(null) 2 | tpl['file.html'] = '
[NAME]
[NAME] ([SIZE])
[UPLOAD]
'; 3 | export default tpl; -------------------------------------------------------------------------------- /src/js/fileup.utils.js: -------------------------------------------------------------------------------- 1 | 2 | let fileUpUtils = { 3 | 4 | 5 | /** 6 | * Проверка на объект 7 | * @param value 8 | */ 9 | isObject: function (value) { 10 | 11 | return typeof value === 'object' && 12 | ! Array.isArray(value) && 13 | value !== null; 14 | }, 15 | 16 | 17 | /** 18 | * Проверка на число 19 | * @param num 20 | * @returns {boolean} 21 | * @private 22 | */ 23 | isNumeric: function(num) { 24 | return (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && ! isNaN(num); 25 | }, 26 | 27 | 28 | /** 29 | * Получение размера файла в байтах 30 | * @param {File} file 31 | * @return {int|null} 32 | */ 33 | getFileSize: function (file) { 34 | 35 | if ( ! (file instanceof File)) { 36 | return null; 37 | } 38 | 39 | return file.size || file.fileSize; 40 | }, 41 | 42 | 43 | /** 44 | * Получение названия файла 45 | * @param {File} file 46 | * @return {string|null} 47 | */ 48 | getFileName: function (file) { 49 | 50 | if ( ! (file instanceof File)) { 51 | return null; 52 | } 53 | 54 | return file.name || file.fileName; 55 | }, 56 | 57 | 58 | /** 59 | * Formatting size 60 | * @param {int} size 61 | * @returns {string} 62 | */ 63 | getSizeHuman: function(size) { 64 | 65 | if ( ! fileUpUtils.isNumeric(size)) { 66 | return ''; 67 | } 68 | 69 | size = Number(size); 70 | 71 | let result = ''; 72 | 73 | if (size >= 1073741824) { 74 | result = (size / 1073741824).toFixed(2) + ' Gb'; 75 | } else if (size >= 1048576) { 76 | result = (size / 1048576).toFixed(2) + ' Mb'; 77 | } else if (size >= 1024) { 78 | result = (size / 1024).toFixed(2) + ' Kb'; 79 | } else if (size >= 0) { 80 | result = size + ' bytes'; 81 | } 82 | 83 | return result; 84 | }, 85 | 86 | 87 | /** 88 | * Создание уникальной строки хэша 89 | * @returns {string} 90 | * @private 91 | */ 92 | hashCode: function() { 93 | return this.crc32((new Date().getTime() + Math.random()).toString()).toString(16); 94 | }, 95 | 96 | 97 | /** 98 | * Hash crc32 99 | * @param str 100 | * @returns {number} 101 | * @private 102 | */ 103 | crc32: function (str) { 104 | 105 | for (var a, o = [], c = 0; c < 256; c++) { 106 | a = c; 107 | for (var f = 0; f < 8; f++) { 108 | a = 1 & a ? 3988292384 ^ a >>> 1 : a >>> 1 109 | } 110 | o[c] = a 111 | } 112 | 113 | for (var n = -1, t = 0; t < str.length; t++) { 114 | n = n >>> 8 ^ o[255 & (n ^ str.charCodeAt(t))] 115 | } 116 | 117 | return (-1 ^ n) >>> 0; 118 | } 119 | } 120 | 121 | export default fileUpUtils; -------------------------------------------------------------------------------- /src/js/lang/en.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUp from "../fileup"; 3 | 4 | fileUp.lang.en = { 5 | upload: 'Upload', 6 | abort: 'Abort', 7 | remove: 'Remove', 8 | complete: 'Complete', 9 | error: 'Error', 10 | errorLoad: 'Error uploading file', 11 | errorBadStatus: 'Error uploading file. Bad request.', 12 | errorFilesLimit: 'The number of selected files exceeds the limit (%filesLimit%)', 13 | errorSizeLimit: 'File "%fileName%" exceeds the size limit (%sizeLimit%)', 14 | errorFileType: 'File "%fileName%" is incorrect', 15 | errorOldBrowser: 'Your browser can not upload files. Update to the latest version' 16 | } -------------------------------------------------------------------------------- /src/js/lang/es.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUp from "../fileup"; 3 | 4 | fileUp.lang.es = { 5 | upload: 'Subir', 6 | abort: 'Cancelar', 7 | remove: 'Eliminar', 8 | complete: 'Cargado', 9 | error: 'Error', 10 | errorLoad: 'Error al cargar el archivo', 11 | errorBadStatus: 'Error al cargar el archivo. Solicitud no válida.', 12 | errorFilesLimit: 'El número de archivo selecccionados excede el límite (%filesLimit%)', 13 | errorSizeLimit: 'El archivo "%fileName%" excede el limite de tamaño (%sizeLimit%)', 14 | errorFileType: 'El archivo "%fileName%" es inválido', 15 | errorOldBrowser: 'Tu navegador no puede subir archivos. Actualiza a la última versión' 16 | } -------------------------------------------------------------------------------- /src/js/lang/pt.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUp from "../fileup"; 3 | 4 | fileUp.lang.pt = { 5 | upload: 'Enviar', 6 | abort: 'Cancelar', 7 | remove: 'Remover', 8 | complete: 'Enviado', 9 | error: 'Erro', 10 | errorLoad: 'Erro ao carregar o arquivo', 11 | errorBadStatus: 'Erro ao carregar o arquivo. Pedido inválido.', 12 | errorFilesLimit: 'O número de arquivos selecionados excede o limite (%filesLimit%)', 13 | errorSizeLimit: 'Arquivo "%fileName%" excede o limite (%sizeLimit%)', 14 | errorFileType: 'Arquivo "%fileName%" inválido', 15 | errorOldBrowser: 'Seu navegador não pode enviar os arquivos. Atualize para a versão mais recente' 16 | } -------------------------------------------------------------------------------- /src/js/lang/ru.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUp from "../fileup"; 3 | 4 | fileUp.lang.ru = { 5 | upload: 'Загрузить', 6 | abort: 'Остановить', 7 | remove: 'Удалить', 8 | complete: 'Готово', 9 | error: 'Ошибка', 10 | errorLoad: 'Ошибка при загрузке файла', 11 | errorBadStatus: 'Ошибка при загрузке файла. Некорректный запрос.', 12 | errorFilesLimit: 'Количество выбранных файлов превышает лимит (%filesLimit%)', 13 | errorSizeLimit: 'Файл "%fileName%" превышает предельный размер (%sizeLimit%)', 14 | errorFileType: 'Файл "%fileName%" является некорректным', 15 | errorOldBrowser: 'Обновите ваш браузер до последней версии' 16 | } -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | 2 | import fileUp from "./fileup"; 3 | 4 | import "./lang/en"; 5 | import "./lang/ru"; 6 | import "./lang/es"; 7 | import "./lang/pt"; 8 | 9 | export default fileUp; --------------------------------------------------------------------------------