├── .gitignore ├── LICENSE ├── Node-red.json ├── README.md ├── lib ├── Flask_Dropzone-1.5.4.dist-info │ ├── INSTALLER │ ├── LICENSE.txt │ ├── METADATA │ ├── RECORD │ ├── WHEEL │ └── top_level.txt ├── Flask_Uploads-0.2.1.dist-info │ ├── INSTALLER │ ├── METADATA │ ├── RECORD │ ├── WHEEL │ ├── top_level.txt │ └── version_info.json ├── README ├── flask_dropzone │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── utils.cpython-38.pyc │ ├── static │ │ ├── dropzone.min.css │ │ └── dropzone.min.js │ └── utils.py └── flask_uploads.py ├── pythoncode └── Flask │ ├── app.py │ ├── haarcascades │ ├── haarcascade_eye.xml │ ├── haarcascade_eye_tree_eyeglasses.xml │ ├── haarcascade_frontalcatface.xml │ ├── haarcascade_frontalcatface_extended.xml │ ├── haarcascade_frontalface_alt.xml │ ├── haarcascade_frontalface_alt2.xml │ ├── haarcascade_frontalface_alt_tree.xml │ ├── haarcascade_frontalface_default.xml │ ├── haarcascade_fullbody.xml │ ├── haarcascade_lefteye_2splits.xml │ ├── haarcascade_licence_plate_rus_16stages.xml │ ├── haarcascade_lowerbody.xml │ ├── haarcascade_profileface.xml │ ├── haarcascade_righteye_2splits.xml │ ├── haarcascade_russian_plate_number.xml │ ├── haarcascade_smile.xml │ └── haarcascade_upperbody.xml │ ├── settings.json │ ├── static │ ├── assets │ │ ├── Logo.png │ │ └── icon.png │ ├── css │ │ └── style.css │ ├── js │ │ └── script.js │ └── libs │ │ ├── bootstrap-4.4.1-dist │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── jquery-3.4.1.min.js │ │ └── popper.min.js │ └── templates │ └── index.html ├── shscripts ├── desktop-launch ├── info.sh └── runserver.sh ├── snap ├── gui │ ├── app.desktop │ └── icon.png └── hooks │ ├── install │ └── remove └── snapcraft.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | # gitginore template for creating Snap packages 4 | # website: https://snapcraft.io/ 5 | 6 | parts/ 7 | prime/ 8 | stage/ 9 | *.snap 10 | *.pptx 11 | *.pdf 12 | 13 | # Snapcraft global state tracking data(automatically generated) 14 | # https://forum.snapcraft.io/t/location-to-save-global-state/768 15 | /snap/.snapcraft/ 16 | 17 | # Source archive packed by `snapcraft cleanbuild` before pushing to the LXD container 18 | /*_source.tar.bz2 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Node-red.json: -------------------------------------------------------------------------------- 1 | [{"id":"53432e5f.a6a51","type":"inject","z":"6fefe7e3.b50988","name":"webcam","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"camerasource\":0, \"cameranumber\":1 }","payloadType":"json","x":180,"y":200,"wires":[["1ae4020b.b90ffe"]]},{"id":"1ae4020b.b90ffe","type":"http request","z":"6fefe7e3.b50988","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"http://localhost:33443/changecamera","tls":"","persist":false,"proxy":"","authType":"","x":430,"y":260,"wires":[["53bc4b13.ac9714"]]},{"id":"53bc4b13.ac9714","type":"debug","z":"6fefe7e3.b50988","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":260,"wires":[]},{"id":"f89b371a.0fb448","type":"inject","z":"6fefe7e3.b50988","name":"IPCAM","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"camerasource\": \"rstp://192.168.1.166:8080/h264_ulaw.sdp\",\"cameranumber\":1}","payloadType":"json","x":170,"y":260,"wires":[["1ae4020b.b90ffe"]]},{"id":"817db188.de0a","type":"http request","z":"6fefe7e3.b50988","name":"","method":"PUT","ret":"txt","paytoqs":"ignore","url":"http://localhost:33443/releasecameras","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":420,"wires":[["f6f9de9e.53ea1"]]},{"id":"93645c45.fedec","type":"http request","z":"6fefe7e3.b50988","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"http://localhost:33443/restartcameras","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":500,"wires":[["51b0a15.a5ba66"]]},{"id":"3c9db45d.cc03bc","type":"inject","z":"6fefe7e3.b50988","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"camerasource\": \"rtsp://freja.hiof.no:1935/rtplive/_definst_/hessdalen02.stream\"}","payloadType":"json","x":170,"y":420,"wires":[["817db188.de0a"]]},{"id":"5d97f10b.24bf3","type":"inject","z":"6fefe7e3.b50988","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"camerasource\": \"rtsp://freja.hiof.no:1935/rtplive/_definst_/hessdalen02.stream\"}","payloadType":"json","x":170,"y":500,"wires":[["93645c45.fedec"]]},{"id":"f6f9de9e.53ea1","type":"debug","z":"6fefe7e3.b50988","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":420,"wires":[]},{"id":"51b0a15.a5ba66","type":"debug","z":"6fefe7e3.b50988","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":500,"wires":[]},{"id":"74918df7.f48cb4","type":"http request","z":"6fefe7e3.b50988","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"http://localhost:33443/savesettings","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":580,"wires":[["d2285230.d18d5"]]},{"id":"bee86006.d1da4","type":"inject","z":"6fefe7e3.b50988","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"camerasource\": \"rtsp://freja.hiof.no:1935/rtplive/_definst_/hessdalen02.stream\"}","payloadType":"json","x":170,"y":580,"wires":[["74918df7.f48cb4"]]},{"id":"d2285230.d18d5","type":"debug","z":"6fefe7e3.b50988","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":580,"wires":[]},{"id":"88506f9c.002a","type":"comment","z":"6fefe7e3.b50988","name":"CAMERA API","info":"","x":170,"y":100,"wires":[]},{"id":"817c189.aa463e8","type":"inject","z":"6fefe7e3.b50988","name":"IPCAM","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"camerasource\": \"rtsp://freja.hiof.no:1935/rtplive/_definst_/hessdalen02.stream\",\"cameranumber\":2}","payloadType":"json","x":170,"y":320,"wires":[["1ae4020b.b90ffe"]]},{"id":"68648731.1413d8","type":"http request","z":"6fefe7e3.b50988","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"http://localhost:33443/changemode","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":740,"wires":[["86f06959.248878"]]},{"id":"86f06959.248878","type":"debug","z":"6fefe7e3.b50988","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":740,"wires":[]},{"id":"494093f8.0da19c","type":"inject","z":"6fefe7e3.b50988","name":"webcam","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"cameramode\":1, \"cameranumber\":1 }","payloadType":"json","x":180,"y":740,"wires":[["68648731.1413d8"]]},{"id":"6677aa10.3d8084","type":"inject","z":"6fefe7e3.b50988","name":"webcam","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"cameramode\":2, \"cameranumber\":1 }","payloadType":"json","x":180,"y":780,"wires":[["68648731.1413d8"]]},{"id":"aa1f50e4.3cd67","type":"inject","z":"6fefe7e3.b50988","name":"webcam","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"cameramode\":1, \"cameranumber\":1 }","payloadType":"json","x":180,"y":700,"wires":[["68648731.1413d8"]]},{"id":"75b73acf.9c05e4","type":"inject","z":"6fefe7e3.b50988","name":"webcam","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"cameramode\":3, \"cameranumber\":1 }","payloadType":"json","x":180,"y":820,"wires":[["68648731.1413d8"]]},{"id":"fb4cd3af.b4551","type":"comment","z":"6fefe7e3.b50988","name":"Set Source","info":"","x":160,"y":140,"wires":[]},{"id":"7a5d16c3.366048","type":"comment","z":"6fefe7e3.b50988","name":"Release Cameras","info":"","x":190,"y":380,"wires":[]},{"id":"fd3495b3.38cf18","type":"comment","z":"6fefe7e3.b50988","name":"Restart Cameras","info":"","x":180,"y":460,"wires":[]},{"id":"b612e178.297a7","type":"comment","z":"6fefe7e3.b50988","name":"Save Configuration","info":"","x":190,"y":540,"wires":[]},{"id":"6ee46441.f0885c","type":"comment","z":"6fefe7e3.b50988","name":"Mode Selection","info":"","x":180,"y":660,"wires":[]},{"id":"fed3e3fb.a02af","type":"inject","z":"6fefe7e3.b50988","name":"webcam","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"cameramode\":0, \"cameranumber\":2 }","payloadType":"json","x":180,"y":880,"wires":[["68648731.1413d8"]]}] 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/opencv-demo-webapp) 3 | 4 | # opencv-demo-webapp-snap 5 | 6 | This webapp has been created to show up the basic components needed to 7 | setup a python based OpenCV APP. Install the app and navigate to http://yourDeviceIp:33443. 8 | 9 | 10 | ### Main components: 11 | 12 | * Opencv (with dependencies): https://opencv.org/ 13 | * libzbar(qrcode algorithm): http://zbar.sourceforge.net/ 14 | * Flask (webserver): https://flask.palletsprojects.com/en/1.1.x/ 15 | * Bootstrap (frontend): https://getbootstrap.com/ 16 | 17 | 18 | 19 | ### Non Snap usage: 20 | 21 | just download the repo, install the dependencies, and run the 22 | app.py file contained in pythoncode/Flask/app.py 23 | -------------------------------------------------------------------------------- /lib/Flask_Dropzone-1.5.4.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /lib/Flask_Dropzone-1.5.4.dist-info/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Grey Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/Flask_Dropzone-1.5.4.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: Flask-Dropzone 3 | Version: 1.5.4 4 | Summary: Upload file in Flask with Dropzone.js. 5 | Home-page: https://github.com/greyli/flask-dropzone 6 | Author: Grey Li 7 | Author-email: withlihui@gmail.com 8 | License: MIT 9 | Keywords: flask extension development upload 10 | Platform: any 11 | Classifier: Development Status :: 3 - Alpha 12 | Classifier: Environment :: Web Environment 13 | Classifier: Intended Audience :: Developers 14 | Classifier: License :: OSI Approved :: MIT License 15 | Classifier: Programming Language :: Python 16 | Classifier: Programming Language :: Python :: 2 17 | Classifier: Programming Language :: Python :: 2.7 18 | Classifier: Programming Language :: Python :: 3 19 | Classifier: Programming Language :: Python :: 3.4 20 | Classifier: Programming Language :: Python :: 3.5 21 | Classifier: Programming Language :: Python :: 3.6 22 | Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content 23 | Classifier: Topic :: Software Development :: Libraries :: Python Modules 24 | Requires-Dist: Flask 25 | 26 | =============== 27 | Flask-Dropzone 28 | =============== 29 | 30 | Flask-Dropzone packages `Dropzone.js 31 | `_ into an extension to add file upload support for Flask. 32 | It can create links to serve Dropzone from a CDN and works with no JavaScript code in your application. 33 | 34 | NOTICE: This extension is built for simple usage, if you need more flexibility, please use Dropzone.js directly. 35 | 36 | Basic Usage 37 | ----------- 38 | 39 | Step 1: Initialize the extension: 40 | 41 | .. code-block:: python 42 | 43 | from flask_dropzone import Dropzone 44 | 45 | dropzone = Dropzone(app) 46 | 47 | 48 | Step 2: In your `` section of your base template add the following code:: 49 | 50 | 51 | {{ dropzone.load_css() }} 52 | 53 | 54 | ... 55 | {{ dropzone.load_js() }} 56 | 57 | 58 | You can assign the version of Dropzone.js through `version` argument, the default value is `5.2.0`. 59 | Step 3: Creating a Drop Zone with `create()`, and configure it with `config()`:: 60 | 61 | {{ dropzone.create(action='the_url_which_handle_uploads') }} 62 | ... 63 | {{ dropzone.config() }} 64 | 65 | Also to edit the action view to yours. 66 | 67 | Beautify Dropzone 68 | ----------------- 69 | 70 | Style it according to your preferences through `style()` method:: 71 | 72 | {{ dropzone.style('border: 2px dashed #0087F7; margin: 10%; min-height: 400px;') }} 73 | 74 | More Detail 75 | ----------- 76 | 77 | Go to `Documentation 78 | `_ , which you can check for more 79 | details. 80 | 81 | 82 | -------------------------------------------------------------------------------- /lib/Flask_Dropzone-1.5.4.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | Flask_Dropzone-1.5.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | Flask_Dropzone-1.5.4.dist-info/LICENSE.txt,sha256=urWe6H0ZCLXr2ZalRweug84-k_8wYWjAXq28Xc9aWsg,1085 3 | Flask_Dropzone-1.5.4.dist-info/METADATA,sha256=fkB8kGV7W-fvscWh4KF_JMYxJV9be0Ywn_hD4Mweexk,2352 4 | Flask_Dropzone-1.5.4.dist-info/RECORD,, 5 | Flask_Dropzone-1.5.4.dist-info/WHEEL,sha256=53VSps8MltPLN_x9Ib61FU2ZSaJKzgrWQqu9rS-Dkgk,116 6 | Flask_Dropzone-1.5.4.dist-info/top_level.txt,sha256=tnmF1XttbqOTalmDk8Ple8B9lemHMKPzXFl96tCdVKE,15 7 | flask_dropzone/__init__.py,sha256=0N1m6v6xPaL7F6vTjHhMK3UyABF-kEi5Jr3Nd6uGE2Q,20192 8 | flask_dropzone/__pycache__/__init__.cpython-38.pyc,, 9 | flask_dropzone/__pycache__/utils.cpython-38.pyc,, 10 | flask_dropzone/static/dropzone.min.css,sha256=C1uHyYDGrQDAk1IbmtnkXnXT_u3PkM9wh0hkpLMhy8U,9718 11 | flask_dropzone/static/dropzone.min.js,sha256=k394u1_NWzM2OVjwVumG22a0prnevsijOBBv3JrDlx4,42791 12 | flask_dropzone/utils.py,sha256=NjwzumNnlEOFWJ5l-nkZhiJ1-ph61N0yEzx8baMu4Gc,708 13 | -------------------------------------------------------------------------------- /lib/Flask_Dropzone-1.5.4.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.33.4) 3 | Root-Is-Purelib: true 4 | Tag: py2-none-any 5 | Tag: py3-none-any 6 | 7 | -------------------------------------------------------------------------------- /lib/Flask_Dropzone-1.5.4.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | flask_dropzone 2 | -------------------------------------------------------------------------------- /lib/Flask_Uploads-0.2.1.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /lib/Flask_Uploads-0.2.1.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: Flask-Uploads 3 | Version: 0.2.1 4 | Summary: Flexible and efficient upload handling for Flask 5 | Home-page: https://github.com/maxcountryman/flask-uploads 6 | Author: Matthew "LeafStorm" Frazier 7 | Author-email: leafstormrush@gmail.com 8 | License: MIT 9 | Platform: any 10 | Classifier: Development Status :: 4 - Beta 11 | Classifier: Environment :: Web Environment 12 | Classifier: Intended Audience :: Developers 13 | Classifier: License :: OSI Approved :: MIT License 14 | Classifier: Operating System :: OS Independent 15 | Classifier: Programming Language :: Python 16 | Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content 17 | Classifier: Topic :: Software Development :: Libraries :: Python Modules 18 | Requires-Dist: Flask (>=0.8.0) 19 | 20 | 21 | Flask-Uploads 22 | ------------- 23 | Flask-Uploads provides flexible upload handling for Flask applications. It 24 | lets you divide your uploads into sets that the application user can publish 25 | separately. 26 | 27 | Links 28 | ````` 29 | * `documentation `_ 30 | * `development version 31 | `_ 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/Flask_Uploads-0.2.1.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | Flask_Uploads-0.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | Flask_Uploads-0.2.1.dist-info/METADATA,sha256=6dYPGwfIUrJixLODOjQQKT0Kipx7TsSCfP4uxeJgFKk,1065 3 | Flask_Uploads-0.2.1.dist-info/RECORD,, 4 | Flask_Uploads-0.2.1.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 5 | Flask_Uploads-0.2.1.dist-info/top_level.txt,sha256=NeRSQvta9S9omfIh9AHymidaUA0FayZq2XSH7r4JNEo,14 6 | Flask_Uploads-0.2.1.dist-info/version_info.json,sha256=6gsOSPdlHLW20Oupts1ylnV96I6Cv5EwuJ5cBTe0Q9k,93 7 | __pycache__/flask_uploads.cpython-38.pyc,, 8 | flask_uploads.py,sha256=TpOXglEsZj4tAAzgXa0U-XQFJRcBBQ8ASK0FUfSivlc,18723 9 | -------------------------------------------------------------------------------- /lib/Flask_Uploads-0.2.1.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.34.2) 3 | Root-Is-Purelib: true 4 | Tag: py3-none-any 5 | 6 | -------------------------------------------------------------------------------- /lib/Flask_Uploads-0.2.1.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | flask_uploads 2 | -------------------------------------------------------------------------------- /lib/Flask_Uploads-0.2.1.dist-info/version_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "release_date": null, 3 | "version": "0.2.1", 4 | "maintainer": "", 5 | "body": "" 6 | } -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | Place here your python site-packages 2 | 3 | follow this guide https://python-forum.io/Thread-Install-a-library-manually 4 | 5 | the entire folder will be copied to the right python folder inside the snap 6 | -------------------------------------------------------------------------------- /lib/flask_dropzone/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask_dropzone 4 | ~~~~~~~~~~~~~~ 5 | 6 | :author: Grey Li 7 | :copyright: (c) 2017 by Grey Li. 8 | :license: MIT, see LICENSE for more details. 9 | """ 10 | import warnings 11 | from flask import Blueprint, current_app, url_for, Markup, render_template_string 12 | 13 | from .utils import random_filename, get_url # noqa 14 | 15 | #: defined normal file type 16 | allowed_file_extensions = { 17 | 'default': 'image/*, audio/*, video/*, text/*, application/*', 18 | 'image': 'image/*', 19 | 'audio': 'audio/*', 20 | 'video': 'video/*', 21 | 'text': 'text/*', 22 | 'app': 'application/*' 23 | } 24 | 25 | 26 | class _Dropzone(object): 27 | 28 | @staticmethod 29 | def load(js_url='', css_url='', version='5.2.0'): 30 | """Load Dropzone resources with given version and init dropzone configuration. 31 | 32 | .. versionchanged:: 1.4.3 33 | Added ``js_url`` and ``css_url`` parameters to pass custom resource URL. 34 | 35 | .. versionchanged:: 1.4.4 36 | This method was deprecated due to inflexible. Now it's divided into three methods: 37 | 1. Use ``load_css()`` to load css resources. 38 | 2. Use ``load_js()`` to load js resources. 39 | 3. Use ``config()`` to configure Dropzone. 40 | 41 | :param js_url: The JavaScript url for Dropzone.js. 42 | :param css_url: The CSS url for Dropzone.js. 43 | :param version: The version of Dropzone.js. 44 | """ 45 | warnings.warn('The method will be removed in 2.0, see docs for more details.') 46 | js_filename = 'dropzone.min.js' 47 | css_filename = 'dropzone.min.css' 48 | 49 | upload_multiple = current_app.config['DROPZONE_UPLOAD_MULTIPLE'] 50 | parallel_uploads = current_app.config['DROPZONE_PARALLEL_UPLOADS'] 51 | 52 | if upload_multiple in [True, 'true', 'True', 1]: 53 | upload_multiple = 'true' 54 | else: 55 | upload_multiple = 'false' 56 | 57 | serve_local = current_app.config['DROPZONE_SERVE_LOCAL'] 58 | size = current_app.config['DROPZONE_MAX_FILE_SIZE'] 59 | param = current_app.config['DROPZONE_INPUT_NAME'] 60 | redirect_view = current_app.config['DROPZONE_REDIRECT_VIEW'] 61 | 62 | if redirect_view is not None: 63 | redirect_js = ''' 64 | this.on("queuecomplete", function(file) { 65 | // Called when all files in the queue finish uploading. 66 | window.location = "%s"; 67 | });''' % url_for(redirect_view) 68 | else: 69 | redirect_js = '' 70 | 71 | if not current_app.config['DROPZONE_ALLOWED_FILE_CUSTOM']: 72 | allowed_type = allowed_file_extensions[ 73 | current_app.config['DROPZONE_ALLOWED_FILE_TYPE']] 74 | else: 75 | allowed_type = current_app.config['DROPZONE_ALLOWED_FILE_TYPE'] 76 | 77 | max_files = current_app.config['DROPZONE_MAX_FILES'] 78 | default_message = current_app.config['DROPZONE_DEFAULT_MESSAGE'] 79 | invalid_file_type = current_app.config['DROPZONE_INVALID_FILE_TYPE'] 80 | file_too_big = current_app.config['DROPZONE_FILE_TOO_BIG'] 81 | server_error = current_app.config['DROPZONE_SERVER_ERROR'] 82 | browser_unsupported = current_app.config['DROPZONE_BROWSER_UNSUPPORTED'] 83 | max_files_exceeded = current_app.config['DROPZONE_MAX_FILE_EXCEED'] 84 | cancelUpload = current_app.config['DROPZONE_CANCEL_UPLOAD'] 85 | removeFile = current_app.config['DROPZONE_REMOVE_FILE'] 86 | cancelConfirmation = current_app.config['DROPZONE_CANCEL_CONFIRMATION'] 87 | uploadCanceled = current_app.config['DROPZONE_UPLOAD_CANCELED'] 88 | 89 | timeout = current_app.config['DROPZONE_TIMEOUT'] 90 | if timeout: 91 | timeout_js = 'timeout: %d,' % timeout 92 | else: 93 | timeout_js = '' 94 | 95 | if serve_local: 96 | js = '\n' % url_for('dropzone.static', filename=js_filename) 97 | css = '\n' % \ 98 | url_for('dropzone.static', filename=css_filename) 99 | else: 100 | js = '\n' % (version, js_filename) 101 | css = '\n' % (version, css_filename) 103 | 104 | if js_url: 105 | js = '\n' % js_url 106 | if css_url: 107 | css = '\n' % css_url 108 | 109 | return Markup(''' 110 | %s%s 132 | ''' % (css, js, redirect_js, upload_multiple, parallel_uploads, param, size, allowed_type, max_files, 133 | default_message, browser_unsupported, invalid_file_type, file_too_big, server_error, 134 | max_files_exceeded, cancelUpload, removeFile, cancelConfirmation, uploadCanceled, timeout_js)) 135 | 136 | @staticmethod 137 | def load_css(css_url=None, version='5.2.0'): 138 | """Load Dropzone's css resources with given version. 139 | 140 | .. versionadded:: 1.4.4 141 | 142 | :param css_url: The CSS url for Dropzone.js. 143 | :param version: The version of Dropzone.js. 144 | """ 145 | css_filename = 'dropzone.min.css' 146 | serve_local = current_app.config['DROPZONE_SERVE_LOCAL'] 147 | 148 | if serve_local: 149 | css = '\n' % \ 150 | url_for('dropzone.static', filename=css_filename) 151 | else: 152 | css = '\n' % (version, css_filename) 154 | 155 | if css_url: 156 | css = '\n' % css_url 157 | return Markup(css) 158 | 159 | @staticmethod 160 | def load_js(js_url=None, version='5.2.0'): 161 | """Load Dropzone's js resources with given version. 162 | 163 | .. versionadded:: 1.4.4 164 | 165 | :param js_url: The JS url for Dropzone.js. 166 | :param version: The version of Dropzone.js. 167 | """ 168 | js_filename = 'dropzone.min.js' 169 | serve_local = current_app.config['DROPZONE_SERVE_LOCAL'] 170 | 171 | if serve_local: 172 | js = '\n' % url_for('dropzone.static', filename=js_filename) 173 | else: 174 | js = '\n' % (version, js_filename) 175 | 176 | if js_url: 177 | js = '\n' % js_url 178 | return Markup(js) 179 | 180 | @staticmethod 181 | def config(redirect_url=None, custom_init='', custom_options='', **kwargs): 182 | """Initialize dropzone configuration. 183 | 184 | .. versionadded:: 1.4.4 185 | 186 | :param redirect_url: The URL to redirect when upload complete. 187 | :param custom_init: Custom javascript code in ``init: function() {}``. 188 | :param custom_options: Custom javascript code in ``Dropzone.options.myDropzone = {}``. 189 | :param **kwargs: Mirror configuration variable, lowercase and without prefix. 190 | For example, ``DROPZONE_UPLOAD_MULTIPLE`` becomes ``upload_multiple`` here. 191 | """ 192 | if custom_init and not custom_init.strip().endswith(';'): 193 | custom_init += ';' 194 | 195 | if custom_options and not custom_options.strip().endswith(','): 196 | custom_options += ',' 197 | 198 | upload_multiple = kwargs.get('upload_multiple', current_app.config['DROPZONE_UPLOAD_MULTIPLE']) 199 | parallel_uploads = kwargs.get('parallel_uploads', current_app.config['DROPZONE_PARALLEL_UPLOADS']) 200 | 201 | if upload_multiple in [True, 'true', 'True', 1]: 202 | upload_multiple = 'true' 203 | else: 204 | upload_multiple = 'false' 205 | 206 | size = kwargs.get('max_file_size', current_app.config['DROPZONE_MAX_FILE_SIZE']) 207 | param = kwargs.get('input_name', current_app.config['DROPZONE_INPUT_NAME']) 208 | redirect_view = kwargs.get('redirect_view', current_app.config['DROPZONE_REDIRECT_VIEW']) 209 | 210 | if redirect_view is not None or redirect_url is not None: 211 | redirect_url = redirect_url or url_for(redirect_view) 212 | redirect_js = ''' 213 | this.on("queuecomplete", function(file) { 214 | // Called when all files in the queue finish uploading. 215 | window.location = "%s"; 216 | });''' % redirect_url 217 | else: 218 | redirect_js = '' 219 | 220 | max_files = kwargs.get('max_files', current_app.config['DROPZONE_MAX_FILES']) 221 | 222 | click_upload = kwargs.get('upload_on_click', current_app.config['DROPZONE_UPLOAD_ON_CLICK']) 223 | button_id = kwargs.get('upload_btn_id', current_app.config['DROPZONE_UPLOAD_BTN_ID']) 224 | in_form = kwargs.get('in_form', current_app.config['DROPZONE_IN_FORM']) 225 | cancelUpload = kwargs.get('cancel_upload', current_app.config['DROPZONE_CANCEL_UPLOAD']) 226 | removeFile = kwargs.get('remove_file', current_app.config['DROPZONE_REMOVE_FILE']) 227 | cancelConfirmation = kwargs.get('cancel_confirmation', current_app.config['DROPZONE_CANCEL_CONFIRMATION']) 228 | uploadCanceled = kwargs.get('upload_canceled', current_app.config['DROPZONE_UPLOAD_CANCELED']) 229 | 230 | if click_upload: 231 | if in_form: 232 | action = get_url(kwargs.get('upload_action', current_app.config['DROPZONE_UPLOAD_ACTION'])) 233 | 234 | click_listener = ''' 235 | dz = this; // Makes sure that 'this' is understood inside the functions below. 236 | 237 | document.getElementById("%s").addEventListener("click", function handler(e) { 238 | e.currentTarget.removeEventListener(e.type, handler); 239 | e.preventDefault(); 240 | e.stopPropagation(); 241 | dz.processQueue(); 242 | }); 243 | this.on("queuecomplete", function(file) { 244 | // Called when all files in the queue finish uploading. 245 | document.getElementById("%s").click(); 246 | }); 247 | ''' % (button_id, button_id) 248 | click_option = ''' 249 | url: "%s", 250 | autoProcessQueue: false, 251 | // addRemoveLinks: true, 252 | ''' % action 253 | else: 254 | click_listener = ''' 255 | dz = this; 256 | document.getElementById("%s").addEventListener("click", function handler(e) {dz.processQueue();}); 257 | ''' % button_id 258 | 259 | click_option = ''' 260 | autoProcessQueue: false, 261 | // addRemoveLinks: true, 262 | ''' 263 | upload_multiple = 'true' 264 | parallel_uploads = max_files if isinstance(max_files, int) else parallel_uploads 265 | else: 266 | click_listener = '' 267 | click_option = '' 268 | 269 | allowed_file_type = kwargs.get('allowed_file_type', current_app.config['DROPZONE_ALLOWED_FILE_TYPE']) 270 | allowed_file_custom = kwargs.get('allowed_file_custom', current_app.config['DROPZONE_ALLOWED_FILE_CUSTOM']) 271 | 272 | if allowed_file_custom: 273 | allowed_type = allowed_file_type 274 | else: 275 | allowed_type = allowed_file_extensions[allowed_file_type] 276 | 277 | default_message = kwargs.get('default_message', current_app.config['DROPZONE_DEFAULT_MESSAGE']) 278 | invalid_file_type = kwargs.get('invalid_file_type', current_app.config['DROPZONE_INVALID_FILE_TYPE']) 279 | file_too_big = kwargs.get('file_too_big', current_app.config['DROPZONE_FILE_TOO_BIG']) 280 | server_error = kwargs.get('server_error', current_app.config['DROPZONE_SERVER_ERROR']) 281 | browser_unsupported = kwargs.get('browser_unsupported', current_app.config['DROPZONE_BROWSER_UNSUPPORTED']) 282 | max_files_exceeded = kwargs.get('max_file_exceeded', current_app.config['DROPZONE_MAX_FILE_EXCEED']) 283 | 284 | timeout = kwargs.get('timeout', current_app.config['DROPZONE_TIMEOUT']) 285 | if timeout: 286 | custom_options += 'timeout: %d,' % timeout 287 | 288 | enable_csrf = kwargs.get('enable_csrf', current_app.config['DROPZONE_ENABLE_CSRF']) 289 | if enable_csrf: 290 | if 'csrf' not in current_app.extensions: 291 | raise RuntimeError("CSRFProtect is not initialized. It's required to enable CSRF protect, \ 292 | see docs for more details.") 293 | csrf_token = render_template_string('{{ csrf_token() }}') 294 | custom_options += 'headers: {"X-CSRF-Token": "%s"},' % csrf_token 295 | 296 | return Markup(''' 323 | ''' % (redirect_js, click_listener, custom_init, click_option, 324 | upload_multiple, parallel_uploads, param, size, allowed_type, max_files, 325 | default_message, browser_unsupported, invalid_file_type, file_too_big, 326 | server_error, max_files_exceeded, cancelUpload, removeFile, cancelConfirmation, 327 | uploadCanceled, custom_options)) 328 | 329 | @staticmethod 330 | def create(action='', csrf=False, action_view='', **kwargs): 331 | """Create a Dropzone form with given action. 332 | 333 | .. versionchanged:: 1.4.2 334 | Added ``csrf`` parameter to enable CSRF protect. 335 | 336 | .. versionchanged:: 1.4.3 337 | Added ``action`` parameter to replace ``action_view``, ``action_view`` was deprecated now. 338 | 339 | .. versionchanged:: 1.5.0 340 | If ``DROPZONE_IN_FORM`` set to ``True``, create ``
`` instead of ``
``. 341 | 342 | .. versionchanged:: 1.5.4 343 | ``csrf`` was deprecated now. 344 | 345 | :param action: The action attribute in ````, pass the url which handle uploads. 346 | :param csrf: Enable CSRF protect or not, same with ``DROPZONE_ENABLE_CSRF``, deprecated since 1.5.4. 347 | :param action_view: The view which handle the post data, deprecated since 1.4.2. 348 | """ 349 | if current_app.config['DROPZONE_IN_FORM']: 350 | return Markup('
') 351 | 352 | if action: 353 | action_url = get_url(action) 354 | else: 355 | warnings.warn('The argument was renamed to "action" and will be removed in 2.0.') 356 | action_url = url_for(action_view, **kwargs) 357 | 358 | if csrf: 359 | warnings.warn('The argument was deprecated and will be removed in 2.0, use DROPZONE_ENABLE_CSRF instead.') 360 | 361 | return Markup('''
''' % action_url) 363 | 364 | @staticmethod 365 | def style(css): 366 | """Add css to dropzone. 367 | 368 | :param css: style sheet code. 369 | """ 370 | return Markup('' % css) 371 | 372 | 373 | class Dropzone(object): 374 | def __init__(self, app=None): 375 | if app is not None: 376 | self.init_app(app) 377 | 378 | def init_app(self, app): 379 | 380 | blueprint = Blueprint('dropzone', __name__, 381 | static_folder='static', 382 | static_url_path='/dropzone' + app.static_url_path) 383 | app.register_blueprint(blueprint) 384 | 385 | if not hasattr(app, 'extensions'): 386 | app.extensions = {} 387 | app.extensions['dropzone'] = _Dropzone 388 | app.context_processor(self.context_processor) 389 | 390 | # settings 391 | app.config.setdefault('DROPZONE_SERVE_LOCAL', False) 392 | app.config.setdefault('DROPZONE_MAX_FILE_SIZE', 3) # MB 393 | app.config.setdefault('DROPZONE_INPUT_NAME', 'file') 394 | app.config.setdefault('DROPZONE_ALLOWED_FILE_CUSTOM', False) 395 | app.config.setdefault('DROPZONE_ALLOWED_FILE_TYPE', 'default') 396 | app.config.setdefault('DROPZONE_MAX_FILES', 'null') 397 | # The timeout to cancel upload request in millisecond, default to 30000 (30 second). 398 | # Set a large number if you need to upload large file. 399 | # .. versionadded: 1.5.0 400 | app.config.setdefault('DROPZONE_TIMEOUT', None) # millisecond, default to 30000 (30 second) 401 | 402 | # The view to redirect when upload was completed. 403 | # .. versionadded:: 1.4.1 404 | app.config.setdefault('DROPZONE_REDIRECT_VIEW', None) 405 | 406 | # Whether to send multiple files in one request. 407 | # In default, each file will send with a request. 408 | # Then you can use ``request.files.getlist('paramName')`` to 409 | # get a list of uploads. 410 | # .. versionadded:: 1.4.1 411 | app.config.setdefault('DROPZONE_UPLOAD_MULTIPLE', False) 412 | 413 | # When ``DROPZONE_UPLOAD_MULTIPLE`` set to True, this will 414 | # defined how many uploads will handled in per request. 415 | # .. versionadded:: 1.4.1 416 | app.config.setdefault('DROPZONE_PARALLEL_UPLOADS', 2) 417 | 418 | # When set to ``True``, it will add a csrf_token hidden field in upload form. 419 | # You have to install Flask-WTF to make it work properly, see details in docs. 420 | # .. versionadded:: 1.4.2 421 | app.config.setdefault('DROPZONE_ENABLE_CSRF', False) 422 | 423 | # Add support to upload files when button was clicked. 424 | # .. versionadded:: 1.5.0 425 | app.config.setdefault('DROPZONE_UPLOAD_ACTION', '') 426 | app.config.setdefault('DROPZONE_UPLOAD_ON_CLICK', False) 427 | app.config.setdefault('DROPZONE_UPLOAD_BTN_ID', 'upload') 428 | 429 | # Add support to create dropzone inside ``
``. 430 | # .. versionadded:: 1.5.0 431 | app.config.setdefault('DROPZONE_IN_FORM', False) 432 | 433 | # messages 434 | app.config.setdefault('DROPZONE_DEFAULT_MESSAGE', "Drop files here or click to upload.") 435 | app.config.setdefault('DROPZONE_INVALID_FILE_TYPE', "You can't upload files of this type.") 436 | app.config.setdefault('DROPZONE_FILE_TOO_BIG', 437 | "File is too big {{filesize}}. Max filesize: {{maxFilesize}}MiB.") 438 | app.config.setdefault('DROPZONE_SERVER_ERROR', "Server error: {{statusCode}}") 439 | app.config.setdefault('DROPZONE_BROWSER_UNSUPPORTED', 440 | "Your browser does not support drag'n'drop file uploads.") 441 | app.config.setdefault('DROPZONE_MAX_FILE_EXCEED', "You can't upload any more files.") 442 | app.config.setdefault('DROPZONE_CANCEL_UPLOAD', "Cancel upload") 443 | app.config.setdefault('DROPZONE_REMOVE_FILE', "Remove file") 444 | app.config.setdefault('DROPZONE_CANCEL_CONFIRMATION', "You really want to delete this file?") 445 | app.config.setdefault('DROPZONE_UPLOAD_CANCELED', "Upload canceled") 446 | 447 | @staticmethod 448 | def context_processor(): 449 | return { 450 | 'dropzone': current_app.extensions['dropzone'] 451 | } 452 | -------------------------------------------------------------------------------- /lib/flask_dropzone/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mauringo/opencv-demo-webapp-snap/68b52b6930d1d31eed764d1e2a32a13073819d99/lib/flask_dropzone/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /lib/flask_dropzone/__pycache__/utils.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mauringo/opencv-demo-webapp-snap/68b52b6930d1d31eed764d1e2a32a13073819d99/lib/flask_dropzone/__pycache__/utils.cpython-38.pyc -------------------------------------------------------------------------------- /lib/flask_dropzone/static/dropzone.min.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-moz-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-moz-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-moz-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:2px solid rgba(0,0,0,0.3);background:white;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:0.5}.dropzone .dz-message{text-align:center;margin:2em 0}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:white}.dropzone .dz-preview.dz-image-preview .dz-details{-webkit-transition:opacity 0.2s linear;-moz-transition:opacity 0.2s linear;-ms-transition:opacity 0.2s linear;-o-transition:opacity 0.2s linear;transition:opacity 0.2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,0.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,0.8);background-color:rgba(255,255,255,0.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid transparent}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:rgba(255,255,255,0.4);padding:0 0.4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{-webkit-transform:scale(1.05, 1.05);-moz-transform:scale(1.05, 1.05);-ms-transform:scale(1.05, 1.05);-o-transform:scale(1.05, 1.05);transform:scale(1.05, 1.05);-webkit-filter:blur(8px);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{-webkit-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;-webkit-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;-webkit-transition:all 0.2s linear;-moz-transition:all 0.2s linear;-ms-transition:all 0.2s linear;-o-transition:all 0.2s linear;transition:all 0.2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;-webkit-transition:opacity 0.4s ease-in;-moz-transition:opacity 0.4s ease-in;-ms-transition:opacity 0.4s ease-in;-o-transition:opacity 0.4s ease-in;transition:opacity 0.4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{-webkit-animation:pulse 6s ease infinite;-moz-animation:pulse 6s ease infinite;-ms-animation:pulse 6s ease infinite;-o-animation:pulse 6s ease infinite;animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:16px;left:50%;top:50%;margin-top:-8px;width:80px;margin-left:-40px;background:rgba(255,255,255,0.9);-webkit-transform:scale(1);border-radius:8px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#333;background:linear-gradient(to bottom, #666, #444);position:absolute;top:0;left:0;bottom:0;width:0;-webkit-transition:width 300ms ease-in-out;-moz-transition:width 300ms ease-in-out;-ms-transition:width 300ms ease-in-out;-o-transition:width 300ms ease-in-out;transition:width 300ms ease-in-out}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;-webkit-transition:opacity 0.3s ease;-moz-transition:opacity 0.3s ease;-ms-transition:opacity 0.3s ease;-o-transition:opacity 0.3s ease;transition:opacity 0.3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#be2626;background:linear-gradient(to bottom, #be2626, #a92222);padding:0.5em 1.2em;color:white}.dropzone .dz-preview .dz-error-message:after{content:'';position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #be2626} 2 | -------------------------------------------------------------------------------- /lib/flask_dropzone/static/dropzone.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function _possibleConstructorReturn(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function _inherits(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function __guard__(a,b){return void 0!==a&&null!==a?b(a):void 0}function __guardMethod__(a,b,c){return void 0!==a&&null!==a&&"function"==typeof a[b]?c(a,b):void 0}var _createClass=function(){function a(a,b){for(var c=0;c1?c-1:0),e=1;e=f.length)break;h=f[g++];h.apply(this,d)}}return this}},{key:"off",value:function(a,b){if(!this._callbacks||0===arguments.length)return this._callbacks={},this;var c=this._callbacks[a];if(!c)return this;if(1===arguments.length)return delete this._callbacks[a],this;for(var d=0;d=c.length)break;e=c[d++];var f=e;if(/(^| )dz-message($| )/.test(f.className)){a=f,f.className="dz-message";break}}a||(a=b.createElement('
'),this.element.appendChild(a));var g=a.getElementsByTagName("span")[0];return g&&(null!=g.textContent?g.textContent=this.options.dictFallbackMessage:null!=g.innerText&&(g.innerText=this.options.dictFallbackMessage)),this.element.appendChild(this.getFallbackForm())},resize:function(a,b,c,d){var e={srcX:0,srcY:0,srcWidth:a.width,srcHeight:a.height},f=a.width/a.height;null==b&&null==c?(b=e.srcWidth,c=e.srcHeight):null==b?b=c*f:null==c&&(c=b/f),b=Math.min(b,e.srcWidth),c=Math.min(c,e.srcHeight);var g=b/c;if(e.srcWidth>b||e.srcHeight>c)if("crop"===d)f>g?(e.srcHeight=a.height,e.srcWidth=e.srcHeight*g):(e.srcWidth=a.width,e.srcHeight=e.srcWidth/g);else{if("contain"!==d)throw new Error("Unknown resizeMethod '"+d+"'");f>g?c=b/f:b=c*f}return e.srcX=(a.width-e.srcWidth)/2,e.srcY=(a.height-e.srcHeight)/2,e.trgWidth=b,e.trgHeight=c,e},transformFile:function(a,b){return(this.options.resizeWidth||this.options.resizeHeight)&&a.type.match(/image.*/)?this.resizeImage(a,this.options.resizeWidth,this.options.resizeHeight,this.options.resizeMethod,b):b(a)},previewTemplate:'
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
',drop:function(a){return this.element.classList.remove("dz-drag-hover")},dragstart:function(a){},dragend:function(a){return this.element.classList.remove("dz-drag-hover")},dragenter:function(a){return this.element.classList.add("dz-drag-hover")},dragover:function(a){return this.element.classList.add("dz-drag-hover")},dragleave:function(a){return this.element.classList.remove("dz-drag-hover")},paste:function(a){},reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(a){var c=this;if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer){a.previewElement=b.createElement(this.options.previewTemplate.trim()),a.previewTemplate=a.previewElement,this.previewsContainer.appendChild(a.previewElement);for(var d=a.previewElement.querySelectorAll("[data-dz-name]"),e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];var g=f;g.textContent=a.name}for(var h=a.previewElement.querySelectorAll("[data-dz-size]"),i=0,h=h;!(i>=h.length);)g=h[i++],g.innerHTML=this.filesize(a.size);this.options.addRemoveLinks&&(a._removeLink=b.createElement(''+this.options.dictRemoveFile+""),a.previewElement.appendChild(a._removeLink));for(var j=function(d){return d.preventDefault(),d.stopPropagation(),a.status===b.UPLOADING?b.confirm(c.options.dictCancelUploadConfirmation,function(){return c.removeFile(a)}):c.options.dictRemoveFileConfirmation?b.confirm(c.options.dictRemoveFileConfirmation,function(){return c.removeFile(a)}):c.removeFile(a)},k=a.previewElement.querySelectorAll("[data-dz-remove]"),l=0,k=k;;){var m;if(l>=k.length)break;m=k[l++];m.addEventListener("click",j)}}},removedfile:function(a){return null!=a.previewElement&&null!=a.previewElement.parentNode&&a.previewElement.parentNode.removeChild(a.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(a,b){if(a.previewElement){a.previewElement.classList.remove("dz-file-preview");for(var c=a.previewElement.querySelectorAll("[data-dz-thumbnail]"),d=0,c=c;;){var e;if(d>=c.length)break;e=c[d++];var f=e;f.alt=a.name,f.src=b}return setTimeout(function(){return a.previewElement.classList.add("dz-image-preview")},1)}},error:function(a,b){if(a.previewElement){a.previewElement.classList.add("dz-error"),"String"!=typeof b&&b.error&&(b=b.error);for(var c=a.previewElement.querySelectorAll("[data-dz-errormessage]"),d=0,c=c;;){var e;if(d>=c.length)break;e=c[d++];e.textContent=b}}},errormultiple:function(){},processing:function(a){if(a.previewElement&&(a.previewElement.classList.add("dz-processing"),a._removeLink))return a._removeLink.textContent=this.options.dictCancelUpload},processingmultiple:function(){},uploadprogress:function(a,b,c){if(a.previewElement)for(var d=a.previewElement.querySelectorAll("[data-dz-uploadprogress]"),e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];var g=f;"PROGRESS"===g.nodeName?g.value=b:g.style.width=b+"%"}},totaluploadprogress:function(){},sending:function(){},sendingmultiple:function(){},success:function(a){if(a.previewElement)return a.previewElement.classList.add("dz-success")},successmultiple:function(){},canceled:function(a){return this.emit("error",a,"Upload canceled.")},canceledmultiple:function(){},complete:function(a){if(a._removeLink&&(a._removeLink.textContent=this.options.dictRemoveFile),a.previewElement)return a.previewElement.classList.add("dz-complete")},completemultiple:function(){},maxfilesexceeded:function(){},maxfilesreached:function(){},queuecomplete:function(){},addedfiles:function(){}},this.prototype._thumbnailQueue=[],this.prototype._processingThumbnail=!1}},{key:"extend",value:function(a){for(var b=arguments.length,c=Array(b>1?b-1:0),d=1;d=e.length)break;g=e[f++];var h=g;for(var i in h){var j=h[i];a[i]=j}}return a}}]),_createClass(b,[{key:"getAcceptedFiles",value:function(){return this.files.filter(function(a){return a.accepted}).map(function(a){return a})}},{key:"getRejectedFiles",value:function(){return this.files.filter(function(a){return!a.accepted}).map(function(a){return a})}},{key:"getFilesWithStatus",value:function(a){return this.files.filter(function(b){return b.status===a}).map(function(a){return a})}},{key:"getQueuedFiles",value:function(){return this.getFilesWithStatus(b.QUEUED)}},{key:"getUploadingFiles",value:function(){return this.getFilesWithStatus(b.UPLOADING)}},{key:"getAddedFiles",value:function(){return this.getFilesWithStatus(b.ADDED)}},{key:"getActiveFiles",value:function(){return this.files.filter(function(a){return a.status===b.UPLOADING||a.status===b.QUEUED}).map(function(a){return a})}},{key:"init",value:function(){var a=this;if("form"===this.element.tagName&&this.element.setAttribute("enctype","multipart/form-data"),this.element.classList.contains("dropzone")&&!this.element.querySelector(".dz-message")&&this.element.appendChild(b.createElement('
'+this.options.dictDefaultMessage+"
")),this.clickableElements.length){!function b(){return a.hiddenFileInput&&a.hiddenFileInput.parentNode.removeChild(a.hiddenFileInput),a.hiddenFileInput=document.createElement("input"),a.hiddenFileInput.setAttribute("type","file"),(null===a.options.maxFiles||a.options.maxFiles>1)&&a.hiddenFileInput.setAttribute("multiple","multiple"),a.hiddenFileInput.className="dz-hidden-input",null!==a.options.acceptedFiles&&a.hiddenFileInput.setAttribute("accept",a.options.acceptedFiles),null!==a.options.capture&&a.hiddenFileInput.setAttribute("capture",a.options.capture),a.hiddenFileInput.style.visibility="hidden",a.hiddenFileInput.style.position="absolute",a.hiddenFileInput.style.top="0",a.hiddenFileInput.style.left="0",a.hiddenFileInput.style.height="0",a.hiddenFileInput.style.width="0",document.querySelector(a.options.hiddenInputContainer).appendChild(a.hiddenFileInput),a.hiddenFileInput.addEventListener("change",function(){var c=a.hiddenFileInput.files;if(c.length)for(var d=c,e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];var g=f;a.addFile(g)}return a.emit("addedfiles",c),b()})}()}this.URL=null!==window.URL?window.URL:window.webkitURL;for(var c=this.events,d=0,c=c;;){var e;if(d>=c.length)break;e=c[d++];var f=e;this.on(f,this.options[f])}this.on("uploadprogress",function(){return a.updateTotalUploadProgress()}),this.on("removedfile",function(){return a.updateTotalUploadProgress()}),this.on("canceled",function(b){return a.emit("complete",b)}),this.on("complete",function(b){if(0===a.getAddedFiles().length&&0===a.getUploadingFiles().length&&0===a.getQueuedFiles().length)return setTimeout(function(){return a.emit("queuecomplete")},0)});var g=function(a){return a.stopPropagation(),a.preventDefault?a.preventDefault():a.returnValue=!1};return this.listeners=[{element:this.element,events:{dragstart:function(b){return a.emit("dragstart",b)},dragenter:function(b){return g(b),a.emit("dragenter",b)},dragover:function(b){var c=void 0;try{c=b.dataTransfer.effectAllowed}catch(a){}return b.dataTransfer.dropEffect="move"===c||"linkMove"===c?"move":"copy",g(b),a.emit("dragover",b)},dragleave:function(b){return a.emit("dragleave",b)},drop:function(b){return g(b),a.drop(b)},dragend:function(b){return a.emit("dragend",b)}}}],this.clickableElements.forEach(function(c){return a.listeners.push({element:c,events:{click:function(d){return(c!==a.element||d.target===a.element||b.elementInside(d.target,a.element.querySelector(".dz-message")))&&a.hiddenFileInput.click(),!0}}})}),this.enable(),this.options.init.call(this)}},{key:"destroy",value:function(){return this.disable(),this.removeAllFiles(!0),(null!=this.hiddenFileInput?this.hiddenFileInput.parentNode:void 0)&&(this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput),this.hiddenFileInput=null),delete this.element.dropzone,b.instances.splice(b.instances.indexOf(this),1)}},{key:"updateTotalUploadProgress",value:function(){var a=void 0,b=0,c=0;if(this.getActiveFiles().length){for(var d=this.getActiveFiles(),e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];var g=f;b+=g.upload.bytesSent,c+=g.upload.total}a=100*b/c}else a=100;return this.emit("totaluploadprogress",a,c,b)}},{key:"_getParamName",value:function(a){return"function"==typeof this.options.paramName?this.options.paramName(a):this.options.paramName+(this.options.uploadMultiple?"["+a+"]":"")}},{key:"_renameFile",value:function(a){return"function"!=typeof this.options.renameFile?a.name:this.options.renameFile(a)}},{key:"getFallbackForm",value:function(){var a=void 0,c=void 0;if(a=this.getExistingFallback())return a;var d='
';this.options.dictFallbackText&&(d+="

"+this.options.dictFallbackText+"

"),d+='
';var e=b.createElement(d);return"FORM"!==this.element.tagName?(c=b.createElement(''),c.appendChild(e)):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=c?c:e}},{key:"getExistingFallback",value:function(){for(var a=["div","form"],b=0;b=b.length)break;d=b[c++];var e=d;if(/(^| )fallback($| )/.test(e.className))return e}}(this.element.getElementsByTagName(d)))return c}}},{key:"setupEventListeners",value:function(){return this.listeners.map(function(a){return function(){var b=[];for(var c in a.events){var d=a.events[c];b.push(a.element.addEventListener(c,d,!1))}return b}()})}},{key:"removeEventListeners",value:function(){return this.listeners.map(function(a){return function(){var b=[];for(var c in a.events){var d=a.events[c];b.push(a.element.removeEventListener(c,d,!1))}return b}()})}},{key:"disable",value:function(){var a=this;return this.clickableElements.forEach(function(a){return a.classList.remove("dz-clickable")}),this.removeEventListeners(),this.files.map(function(b){return a.cancelUpload(b)})}},{key:"enable",value:function(){return this.clickableElements.forEach(function(a){return a.classList.add("dz-clickable")}),this.setupEventListeners()}},{key:"filesize",value:function(a){var b=0,c="b";if(a>0){for(var d=["tb","gb","mb","kb","b"],e=0;e=Math.pow(this.options.filesizeBase,4-e)/10){b=a/Math.pow(this.options.filesizeBase,4-e),c=f;break}}b=Math.round(10*b)/10}return""+b+" "+this.options.dictFileSizeUnits[c]}},{key:"_updateMaxFilesReachedClass",value:function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")}},{key:"drop",value:function(a){if(a.dataTransfer){this.emit("drop",a);var b=a.dataTransfer.files;if(this.emit("addedfiles",b),b.length){var c=a.dataTransfer.items;c&&c.length&&null!=c[0].webkitGetAsEntry?this._addFilesFromItems(c):this.handleFiles(b)}}}},{key:"paste",value:function(a){if(null!=__guard__(null!=a?a.clipboardData:void 0,function(a){return a.items})){this.emit("paste",a);var b=a.clipboardData.items;return b.length?this._addFilesFromItems(b):void 0}}},{key:"handleFiles",value:function(a){var b=this;return a.map(function(a){return b.addFile(a)})}},{key:"_addFilesFromItems",value:function(a){var b=this;return function(){for(var c=[],d=a,e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];var g,h=f;null!=h.webkitGetAsEntry&&(g=h.webkitGetAsEntry())?g.isFile?c.push(b.addFile(h.getAsFile())):g.isDirectory?c.push(b._addFilesFromDirectory(g,g.name)):c.push(void 0):null!=h.getAsFile&&(null==h.kind||"file"===h.kind)?c.push(b.addFile(h.getAsFile())):c.push(void 0)}return c}()}},{key:"_addFilesFromDirectory",value:function(a,b){var c=this,d=a.createReader(),e=function(a){return __guardMethod__(console,"log",function(b){return b.log(a)})};return function a(){return d.readEntries(function(d){if(d.length>0){for(var e=d,f=0,e=e;;){var g;if(f>=e.length)break;g=e[f++];var h=g;h.isFile?h.file(function(a){if(!c.options.ignoreHiddenFiles||"."!==a.name.substring(0,1))return a.fullPath=b+"/"+a.name,c.addFile(a)}):h.isDirectory&&c._addFilesFromDirectory(h,b+"/"+h.name)}a()}return null},e)}()}},{key:"accept",value:function(a,c){return a.size>1024*this.options.maxFilesize*1024?c(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(a.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):b.isValidFile(a,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(c(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",a)):this.options.accept.call(this,a,c):c(this.options.dictInvalidFileType)}},{key:"addFile",value:function(a){var c=this;return a.upload={uuid:b.uuidv4(),progress:0,total:a.size,bytesSent:0,filename:this._renameFile(a),chunked:this.options.chunking&&(this.options.forceChunking||a.size>this.options.chunkSize),totalChunkCount:Math.ceil(a.size/this.options.chunkSize)},this.files.push(a),a.status=b.ADDED,this.emit("addedfile",a),this._enqueueThumbnail(a),this.accept(a,function(b){return b?(a.accepted=!1,c._errorProcessing([a],b)):(a.accepted=!0,c.options.autoQueue&&c.enqueueFile(a)),c._updateMaxFilesReachedClass()})}},{key:"enqueueFiles",value:function(a){for(var b=a,c=0,b=b;;){var d;if(c>=b.length)break;d=b[c++];var e=d;this.enqueueFile(e)}return null}},{key:"enqueueFile",value:function(a){var c=this;if(a.status!==b.ADDED||!0!==a.accepted)throw new Error("This file can't be queued because it has already been processed or was rejected.");if(a.status=b.QUEUED,this.options.autoProcessQueue)return setTimeout(function(){return c.processQueue()},0)}},{key:"_enqueueThumbnail",value:function(a){var b=this;if(this.options.createImageThumbnails&&a.type.match(/image.*/)&&a.size<=1024*this.options.maxThumbnailFilesize*1024)return this._thumbnailQueue.push(a),setTimeout(function(){return b._processThumbnailQueue()},0)}},{key:"_processThumbnailQueue",value:function(){var a=this;if(!this._processingThumbnail&&0!==this._thumbnailQueue.length){this._processingThumbnail=!0;var b=this._thumbnailQueue.shift();return this.createThumbnail(b,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,!0,function(c){return a.emit("thumbnail",b,c),a._processingThumbnail=!1,a._processThumbnailQueue()})}}},{key:"removeFile",value:function(a){if(a.status===b.UPLOADING&&this.cancelUpload(a),this.files=without(this.files,a),this.emit("removedfile",a),0===this.files.length)return this.emit("reset")}},{key:"removeAllFiles",value:function(a){null==a&&(a=!1);for(var c=this.files.slice(),d=0,c=c;;){var e;if(d>=c.length)break;e=c[d++];var f=e;(f.status!==b.UPLOADING||a)&&this.removeFile(f)}return null}},{key:"resizeImage",value:function(a,c,d,e,f){var g=this;return this.createThumbnail(a,c,d,e,!1,function(c,d){if(null===d)return f(a);var e=g.options.resizeMimeType;null==e&&(e=a.type);var h=d.toDataURL(e,g.options.resizeQuality);return"image/jpeg"!==e&&"image/jpg"!==e||(h=ExifRestore.restore(a.dataURL,h)),f(b.dataURItoBlob(h))})}},{key:"createThumbnail",value:function(a,b,c,d,e,f){var g=this,h=new FileReader;return h.onload=function(){return a.dataURL=h.result,"image/svg+xml"===a.type?void(null!=f&&f(h.result)):g.createThumbnailFromUrl(a,b,c,d,e,f)},h.readAsDataURL(a)}},{key:"createThumbnailFromUrl",value:function(a,b,c,d,e,f,g){var h=this,i=document.createElement("img");return g&&(i.crossOrigin=g),i.onload=function(){var g=function(a){return a(1)};return"undefined"!=typeof EXIF&&null!==EXIF&&e&&(g=function(a){return EXIF.getData(i,function(){return a(EXIF.getTag(this,"Orientation"))})}),g(function(e){a.width=i.width,a.height=i.height;var g=h.options.resize.call(h,a,b,c,d),j=document.createElement("canvas"),k=j.getContext("2d");switch(j.width=g.trgWidth,j.height=g.trgHeight,e>4&&(j.width=g.trgHeight,j.height=g.trgWidth),e){case 2:k.translate(j.width,0),k.scale(-1,1);break;case 3:k.translate(j.width,j.height),k.rotate(Math.PI);break;case 4:k.translate(0,j.height),k.scale(1,-1);break;case 5:k.rotate(.5*Math.PI),k.scale(1,-1);break;case 6:k.rotate(.5*Math.PI),k.translate(0,-j.height);break;case 7:k.rotate(.5*Math.PI),k.translate(j.width,-j.height),k.scale(-1,1);break;case 8:k.rotate(-.5*Math.PI),k.translate(-j.width,0)}drawImageIOSFix(k,i,null!=g.srcX?g.srcX:0,null!=g.srcY?g.srcY:0,g.srcWidth,g.srcHeight,null!=g.trgX?g.trgX:0,null!=g.trgY?g.trgY:0,g.trgWidth,g.trgHeight);var l=j.toDataURL("image/png");if(null!=f)return f(l,j)})},null!=f&&(i.onerror=f),i.src=a.dataURL}},{key:"processQueue",value:function(){var a=this.options.parallelUploads,b=this.getUploadingFiles().length,c=b;if(!(b>=a)){var d=this.getQueuedFiles();if(d.length>0){if(this.options.uploadMultiple)return this.processFiles(d.slice(0,a-b));for(;c=c.length)break;e=c[d++];var f=e;f.processing=!0,f.status=b.UPLOADING,this.emit("processing",f)}return this.options.uploadMultiple&&this.emit("processingmultiple",a),this.uploadFiles(a)}},{key:"_getFilesWithXhr",value:function(a){return this.files.filter(function(b){return b.xhr===a}).map(function(a){return a})}},{key:"cancelUpload",value:function(a){if(a.status===b.UPLOADING){for(var c=this._getFilesWithXhr(a.xhr),d=c,e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];f.status=b.CANCELED}void 0!==a.xhr&&a.xhr.abort();for(var g=c,h=0,g=g;;){var i;if(h>=g.length)break;i=g[h++];var j=i;this.emit("canceled",j)}this.options.uploadMultiple&&this.emit("canceledmultiple",c)}else a.status!==b.ADDED&&a.status!==b.QUEUED||(a.status=b.CANCELED,this.emit("canceled",a),this.options.uploadMultiple&&this.emit("canceledmultiple",[a]));if(this.options.autoProcessQueue)return this.processQueue()}},{key:"resolveOption",value:function(a){if("function"==typeof a){for(var b=arguments.length,c=Array(b>1?b-1:0),d=1;d=e.upload.totalChunkCount)){g++;var h=d*c.options.chunkSize,i=Math.min(h+c.options.chunkSize,e.size),j={name:c._getParamName(0),data:f.webkitSlice?f.webkitSlice(h,i):f.slice(h,i),filename:e.upload.filename,chunkIndex:d};e.upload.chunks[d]={file:e,index:d,dataBlock:j,status:b.UPLOADING,progress:0,retries:0},c._uploadData(a,[j])}};if(e.upload.finishedChunkUpload=function(d){var f=!0;d.status=b.SUCCESS,d.dataBlock=null;for(var g=0;g=f.length)break;h=f[g++];h.xhr=e}a[0].upload.chunked&&(a[0].upload.chunks[c[0].chunkIndex].xhr=e);var i=this.resolveOption(this.options.method,a),j=this.resolveOption(this.options.url,a);e.open(i,j,!0),e.timeout=this.resolveOption(this.options.timeout,a),e.withCredentials=!!this.options.withCredentials,e.onload=function(b){d._finishedUploading(a,e,b)},e.onerror=function(){d._handleUploadError(a,e)},(null!=e.upload?e.upload:e).onprogress=function(b){return d._updateFilesUploadProgress(a,e,b)};var k={Accept:"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};this.options.headers&&b.extend(k,this.options.headers);for(var l in k){var m=k[l];m&&e.setRequestHeader(l,m)}var n=new FormData;if(this.options.params){var o=this.options.params;"function"==typeof o&&(o=o.call(this,a,e,a[0].upload.chunked?this._getChunk(a[0],e):null));for(var p in o){var q=o[p];n.append(p,q)}}for(var r=a,s=0,r=r;;){var t;if(s>=r.length)break;t=r[s++];var u=t;this.emit("sending",u,e,n)}this.options.uploadMultiple&&this.emit("sendingmultiple",a,e,n),this._addFormElementData(n);for(var v=0;v=b.length)break;d=b[c++];var e=d,f=e.getAttribute("name"),g=e.getAttribute("type");if(g&&(g=g.toLowerCase()),void 0!==f&&null!==f)if("SELECT"===e.tagName&&e.hasAttribute("multiple"))for(var h=e.options,i=0,h=h;;){var j;if(i>=h.length)break;j=h[i++];var k=j;k.selected&&a.append(f,k.value)}else(!g||"checkbox"!==g&&"radio"!==g||e.checked)&&a.append(f,e.value)}}},{key:"_updateFilesUploadProgress",value:function(a,b,c){var d=void 0;if(void 0!==c){if(d=100*c.loaded/c.total,a[0].upload.chunked){var e=a[0],f=this._getChunk(e,b);f.progress=d,f.total=c.total,f.bytesSent=c.loaded;e.upload.progress=0,e.upload.total=0,e.upload.bytesSent=0;for(var g=0;g=h.length)break;j=h[i++];var k=j;k.upload.progress=d,k.upload.total=c.total,k.upload.bytesSent=c.loaded}for(var l=a,m=0,l=l;;){var n;if(m>=l.length)break;n=l[m++];var o=n;this.emit("uploadprogress",o,o.upload.progress,o.upload.bytesSent)}}else{var p=!0;d=100;for(var q=a,r=0,q=q;;){var s;if(r>=q.length)break;s=q[r++];var t=s;100===t.upload.progress&&t.upload.bytesSent===t.upload.total||(p=!1),t.upload.progress=d,t.upload.bytesSent=t.upload.total}if(p)return;for(var u=a,v=0,u=u;;){var w;if(v>=u.length)break;w=u[v++];var x=w;this.emit("uploadprogress",x,d,x.upload.bytesSent)}}}},{key:"_finishedUploading",value:function(a,c,d){var e=void 0;if(a[0].status!==b.CANCELED&&4===c.readyState){if("arraybuffer"!==c.responseType&&"blob"!==c.responseType&&(e=c.responseText,c.getResponseHeader("content-type")&&~c.getResponseHeader("content-type").indexOf("application/json")))try{e=JSON.parse(e)}catch(a){d=a,e="Invalid JSON response from server."}this._updateFilesUploadProgress(a),200<=c.status&&c.status<300?a[0].upload.chunked?a[0].upload.finishedChunkUpload(this._getChunk(a[0],c)):this._finished(a,e,d):this._handleUploadError(a,c,e)}}},{key:"_handleUploadError",value:function(a,c,d){if(a[0].status!==b.CANCELED){if(a[0].upload.chunked&&this.options.retryChunks){var e=this._getChunk(a[0],c);if(e.retries++=f.length)break;f[g++];this._errorProcessing(a,d||this.options.dictResponseError.replace("{{statusCode}}",c.status),c)}}}},{key:"submitRequest",value:function(a,b,c){a.send(b)}},{key:"_finished",value:function(a,c,d){for(var e=a,f=0,e=e;;){var g;if(f>=e.length)break;g=e[f++];var h=g;h.status=b.SUCCESS,this.emit("success",h,c,d),this.emit("complete",h)}if(this.options.uploadMultiple&&(this.emit("successmultiple",a,c,d),this.emit("completemultiple",a)),this.options.autoProcessQueue)return this.processQueue()}},{key:"_errorProcessing",value:function(a,c,d){for(var e=a,f=0,e=e;;){var g;if(f>=e.length)break;g=e[f++];var h=g;h.status=b.ERROR,this.emit("error",h,c,d),this.emit("complete",h)}if(this.options.uploadMultiple&&(this.emit("errormultiple",a,c,d),this.emit("completemultiple",a)),this.options.autoProcessQueue)return this.processQueue()}}],[{key:"uuidv4",value:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0;return("x"===a?b:3&b|8).toString(16)})}}]),b}(Emitter);Dropzone.initClass(),Dropzone.version="5.2.0",Dropzone.options={},Dropzone.optionsForElement=function(a){return a.getAttribute("id")?Dropzone.options[camelize(a.getAttribute("id"))]:void 0},Dropzone.instances=[],Dropzone.forElement=function(a){if("string"==typeof a&&(a=document.querySelector(a)),null==(null!=a?a.dropzone:void 0))throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");return a.dropzone},Dropzone.autoDiscover=!0,Dropzone.discover=function(){var a=void 0;if(document.querySelectorAll)a=document.querySelectorAll(".dropzone");else{a=[];var b=function(b){return function(){for(var c=[],d=b,e=0,d=d;;){var f;if(e>=d.length)break;f=d[e++];var g=f;/(^| )dropzone($| )/.test(g.className)?c.push(a.push(g)):c.push(void 0)}return c}()};b(document.getElementsByTagName("div")),b(document.getElementsByTagName("form"))}return function(){for(var b=[],c=a,d=0,c=c;;){var e;if(d>=c.length)break;e=c[d++];var f=e;!1!==Dropzone.optionsForElement(f)?b.push(new Dropzone(f)):b.push(void 0)}return b}()},Dropzone.blacklistedBrowsers=[/opera.*(Macintosh|Windows Phone).*version\/12/i],Dropzone.isBrowserSupported=function(){var a=!0;if(window.File&&window.FileReader&&window.FileList&&window.Blob&&window.FormData&&document.querySelector)if("classList"in document.createElement("a"))for(var b=Dropzone.blacklistedBrowsers,c=0,b=b;;){var d;if(c>=b.length)break;d=b[c++];var e=d;e.test(navigator.userAgent)&&(a=!1)}else a=!1;else a=!1;return a},Dropzone.dataURItoBlob=function(a){for(var b=atob(a.split(",")[1]),c=a.split(",")[0].split(":")[1].split(";")[0],d=new ArrayBuffer(b.length),e=new Uint8Array(d),f=0,g=b.length,h=0<=g;h?f<=g:f>=g;h?f++:f--)e[f]=b.charCodeAt(f);return new Blob([d],{type:c})};var without=function(a,b){return a.filter(function(a){return a!==b}).map(function(a){return a})},camelize=function(a){return a.replace(/[\-_](\w)/g,function(a){return a.charAt(1).toUpperCase()})};Dropzone.createElement=function(a){var b=document.createElement("div");return b.innerHTML=a,b.childNodes[0]},Dropzone.elementInside=function(a,b){if(a===b)return!0;for(;a=a.parentNode;)if(a===b)return!0;return!1},Dropzone.getElement=function(a,b){var c=void 0;if("string"==typeof a?c=document.querySelector(a):null!=a.nodeType&&(c=a),null==c)throw new Error("Invalid `"+b+"` option provided. Please provide a CSS selector or a plain HTML element.");return c},Dropzone.getElements=function(a,b){var c=void 0,d=void 0;if(a instanceof Array){d=[];try{for(var e=a,f=0,e=e;!(f>=e.length);)c=e[f++],d.push(this.getElement(c,b))}catch(a){d=null}}else if("string"==typeof a){d=[];for(var g=document.querySelectorAll(a),h=0,g=g;!(h>=g.length);)c=g[h++],d.push(c)}else null!=a.nodeType&&(d=[a]);if(null==d||!d.length)throw new Error("Invalid `"+b+"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");return d},Dropzone.confirm=function(a,b,c){return window.confirm(a)?b():null!=c?c():void 0},Dropzone.isValidFile=function(a,b){if(!b)return!0;b=b.split(",");for(var c=a.type,d=c.replace(/\/.*$/,""),e=b,f=0,e=e;;){var g;if(f>=e.length)break;g=e[f++];var h=g;if(h=h.trim(),"."===h.charAt(0)){if(-1!==a.name.toLowerCase().indexOf(h.toLowerCase(),a.name.length-h.length))return!0}else if(/\/\*$/.test(h)){if(d===h.replace(/\/.*$/,""))return!0}else if(c===h)return!0}return!1},"undefined"!=typeof jQuery&&null!==jQuery&&(jQuery.fn.dropzone=function(a){return this.each(function(){return new Dropzone(this,a)})}),"undefined"!=typeof module&&null!==module?module.exports=Dropzone:window.Dropzone=Dropzone,Dropzone.ADDED="added",Dropzone.QUEUED="queued",Dropzone.ACCEPTED=Dropzone.QUEUED,Dropzone.UPLOADING="uploading",Dropzone.PROCESSING=Dropzone.UPLOADING,Dropzone.CANCELED="canceled",Dropzone.ERROR="error",Dropzone.SUCCESS="success";var detectVerticalSquash=function(a){var b=(a.naturalWidth,a.naturalHeight),c=document.createElement("canvas");c.width=1,c.height=b;var d=c.getContext("2d");d.drawImage(a,0,0);for(var e=d.getImageData(1,0,1,b),f=e.data,g=0,h=b,i=b;i>g;){0===f[4*(i-1)+3]?h=i:g=i,i=h+g>>1}var j=i/b;return 0===j?1:j},drawImageIOSFix=function(a,b,c,d,e,f,g,h,i,j){var k=detectVerticalSquash(b);return a.drawImage(b,c,d,e,f,g,h,i,j/k)},ExifRestore=function(){function a(){_classCallCheck(this,a)}return _createClass(a,null,[{key:"initClass",value:function(){this.KEY_STR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}},{key:"encode64",value:function(a){for(var b="",c=void 0,d=void 0,e="",f=void 0,g=void 0,h=void 0,i="",j=0;;)if(c=a[j++],d=a[j++],e=a[j++],f=c>>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,isNaN(d)?h=i=64:isNaN(e)&&(i=64),b=b+this.KEY_STR.charAt(f)+this.KEY_STR.charAt(g)+this.KEY_STR.charAt(h)+this.KEY_STR.charAt(i),c=d=e="",f=g=h=i="",!(ja.length)break}return c}},{key:"decode64",value:function(a){var b=void 0,c=void 0,d="",e=void 0,f=void 0,g=void 0,h="",i=0,j=[],k=/[^A-Za-z0-9\+\/\=]/g;for(k.exec(a)&&console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."),a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");;)if(e=this.KEY_STR.indexOf(a.charAt(i++)),f=this.KEY_STR.indexOf(a.charAt(i++)),g=this.KEY_STR.indexOf(a.charAt(i++)),h=this.KEY_STR.indexOf(a.charAt(i++)),b=e<<2|f>>4,c=(15&f)<<4|g>>2,d=(3&g)<<6|h,j.push(b),64!==g&&j.push(c),64!==h&&j.push(d),b=c=d="",e=f=g=h="",!(i 6 | :copyright: (c) 2017 by Grey Li. 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | import os 10 | import uuid 11 | 12 | from flask import url_for 13 | 14 | 15 | def get_url(endpoint_or_url): 16 | if endpoint_or_url == '': 17 | return 18 | if endpoint_or_url.startswith(('https://', 'http://', '/')): 19 | return endpoint_or_url 20 | else: 21 | return url_for(endpoint_or_url) 22 | 23 | 24 | #: generate a random filename, replacement for werkzeug.secure_filename 25 | def random_filename(old_filename): 26 | ext = os.path.splitext(old_filename)[1] 27 | new_filename = uuid.uuid4().hex + ext 28 | return new_filename 29 | -------------------------------------------------------------------------------- /lib/flask_uploads.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flaskext.uploads 4 | ================ 5 | This module provides upload support for Flask. The basic pattern is to set up 6 | an `UploadSet` object and upload your files to it. 7 | 8 | :copyright: 2010 Matthew "LeafStorm" Frazier 9 | :license: MIT/X11, see LICENSE for details 10 | """ 11 | 12 | import sys 13 | 14 | PY3 = sys.version_info[0] == 3 15 | 16 | if PY3: 17 | string_types = str, 18 | else: 19 | string_types = basestring, 20 | 21 | import os.path 22 | import posixpath 23 | 24 | from flask import current_app, send_from_directory, abort, url_for 25 | from itertools import chain 26 | from werkzeug.utils import secure_filename 27 | from werkzeug.datastructures import FileStorage 28 | 29 | from flask import Blueprint 30 | 31 | # Extension presets 32 | 33 | #: This just contains plain text files (.txt). 34 | TEXT = ('txt',) 35 | 36 | #: This contains various office document formats (.rtf, .odf, .ods, .gnumeric, 37 | #: .abw, .doc, .docx, .xls, and .xlsx). Note that the macro-enabled versions 38 | #: of Microsoft Office 2007 files are not included. 39 | DOCUMENTS = tuple('rtf odf ods gnumeric abw doc docx xls xlsx'.split()) 40 | 41 | #: This contains basic image types that are viewable from most browsers (.jpg, 42 | #: .jpe, .jpeg, .png, .gif, .svg, and .bmp). 43 | IMAGES = tuple('jpg jpe jpeg png gif svg bmp'.split()) 44 | 45 | #: This contains audio file types (.wav, .mp3, .aac, .ogg, .oga, and .flac). 46 | AUDIO = tuple('wav mp3 aac ogg oga flac'.split()) 47 | 48 | #: This is for structured data files (.csv, .ini, .json, .plist, .xml, .yaml, 49 | #: and .yml). 50 | DATA = tuple('csv ini json plist xml yaml yml'.split()) 51 | 52 | #: This contains various types of scripts (.js, .php, .pl, .py .rb, and .sh). 53 | #: If your Web server has PHP installed and set to auto-run, you might want to 54 | #: add ``php`` to the DENY setting. 55 | SCRIPTS = tuple('js php pl py rb sh'.split()) 56 | 57 | #: This contains archive and compression formats (.gz, .bz2, .zip, .tar, 58 | #: .tgz, .txz, and .7z). 59 | ARCHIVES = tuple('gz bz2 zip tar tgz txz 7z'.split()) 60 | 61 | #: This contains shared libraries and executable files (.so, .exe and .dll). 62 | #: Most of the time, you will not want to allow this - it's better suited for 63 | #: use with `AllExcept`. 64 | EXECUTABLES = tuple('so exe dll'.split()) 65 | 66 | #: The default allowed extensions - `TEXT`, `DOCUMENTS`, `DATA`, and `IMAGES`. 67 | DEFAULTS = TEXT + DOCUMENTS + IMAGES + DATA 68 | 69 | 70 | class UploadNotAllowed(Exception): 71 | """ 72 | This exception is raised if the upload was not allowed. You should catch 73 | it in your view code and display an appropriate message to the user. 74 | """ 75 | 76 | 77 | def tuple_from(*iters): 78 | return tuple(itertools.chain(*iters)) 79 | 80 | 81 | def extension(filename): 82 | ext = os.path.splitext(filename)[1] 83 | if ext.startswith('.'): 84 | # os.path.splitext retains . separator 85 | ext = ext[1:] 86 | return ext 87 | 88 | 89 | def lowercase_ext(filename): 90 | """ 91 | This is a helper used by UploadSet.save to provide lowercase extensions for 92 | all processed files, to compare with configured extensions in the same 93 | case. 94 | 95 | .. versionchanged:: 0.1.4 96 | Filenames without extensions are no longer lowercased, only the 97 | extension is returned in lowercase, if an extension exists. 98 | 99 | :param filename: The filename to ensure has a lowercase extension. 100 | """ 101 | if '.' in filename: 102 | main, ext = os.path.splitext(filename) 103 | return main + ext.lower() 104 | # For consistency with os.path.splitext, 105 | # do not treat a filename without an extension as an extension. 106 | # That is, do not return filename.lower(). 107 | return filename 108 | 109 | 110 | def addslash(url): 111 | if url.endswith('/'): 112 | return url 113 | return url + '/' 114 | 115 | 116 | def patch_request_class(app, size=64 * 1024 * 1024): 117 | """ 118 | By default, Flask will accept uploads to an arbitrary size. While Werkzeug 119 | switches uploads from memory to a temporary file when they hit 500 KiB, 120 | it's still possible for someone to overload your disk space with a 121 | gigantic file. 122 | 123 | This patches the app's request class's 124 | `~werkzeug.BaseRequest.max_content_length` attribute so that any upload 125 | larger than the given size is rejected with an HTTP error. 126 | 127 | .. note:: 128 | 129 | In Flask 0.6, you can do this by setting the `MAX_CONTENT_LENGTH` 130 | setting, without patching the request class. To emulate this behavior, 131 | you can pass `None` as the size (you must pass it explicitly). That is 132 | the best way to call this function, as it won't break the Flask 0.6 133 | functionality if it exists. 134 | 135 | .. versionchanged:: 0.1.1 136 | 137 | :param app: The app to patch the request class of. 138 | :param size: The maximum size to accept, in bytes. The default is 64 MiB. 139 | If it is `None`, the app's `MAX_CONTENT_LENGTH` configuration 140 | setting will be used to patch. 141 | """ 142 | if size is None: 143 | if isinstance(app.request_class.__dict__['max_content_length'], 144 | property): 145 | return 146 | size = app.config.get('MAX_CONTENT_LENGTH') 147 | reqclass = app.request_class 148 | patched = type(reqclass.__name__, (reqclass,), 149 | {'max_content_length': size}) 150 | app.request_class = patched 151 | 152 | 153 | def config_for_set(uset, app, defaults=None): 154 | """ 155 | This is a helper function for `configure_uploads` that extracts the 156 | configuration for a single set. 157 | 158 | :param uset: The upload set. 159 | :param app: The app to load the configuration from. 160 | :param defaults: A dict with keys `url` and `dest` from the 161 | `UPLOADS_DEFAULT_DEST` and `DEFAULT_UPLOADS_URL` 162 | settings. 163 | """ 164 | config = app.config 165 | prefix = 'UPLOADED_%s_' % uset.name.upper() 166 | using_defaults = False 167 | if defaults is None: 168 | defaults = dict(dest=None, url=None) 169 | 170 | allow_extns = tuple(config.get(prefix + 'ALLOW', ())) 171 | deny_extns = tuple(config.get(prefix + 'DENY', ())) 172 | destination = config.get(prefix + 'DEST') 173 | base_url = config.get(prefix + 'URL') 174 | 175 | if destination is None: 176 | # the upload set's destination wasn't given 177 | if uset.default_dest: 178 | # use the "default_dest" callable 179 | destination = uset.default_dest(app) 180 | if destination is None: # still 181 | # use the default dest from the config 182 | if defaults['dest'] is not None: 183 | using_defaults = True 184 | destination = os.path.join(defaults['dest'], uset.name) 185 | else: 186 | raise RuntimeError("no destination for set %s" % uset.name) 187 | 188 | if base_url is None and using_defaults and defaults['url']: 189 | base_url = addslash(defaults['url']) + uset.name + '/' 190 | 191 | return UploadConfiguration(destination, base_url, allow_extns, deny_extns) 192 | 193 | 194 | def configure_uploads(app, upload_sets): 195 | """ 196 | Call this after the app has been configured. It will go through all the 197 | upload sets, get their configuration, and store the configuration on the 198 | app. It will also register the uploads module if it hasn't been set. This 199 | can be called multiple times with different upload sets. 200 | 201 | .. versionchanged:: 0.1.3 202 | The uploads module/blueprint will only be registered if it is needed 203 | to serve the upload sets. 204 | 205 | :param app: The `~flask.Flask` instance to get the configuration from. 206 | :param upload_sets: The `UploadSet` instances to configure. 207 | """ 208 | if isinstance(upload_sets, UploadSet): 209 | upload_sets = (upload_sets,) 210 | 211 | if not hasattr(app, 'upload_set_config'): 212 | app.upload_set_config = {} 213 | set_config = app.upload_set_config 214 | defaults = dict(dest=app.config.get('UPLOADS_DEFAULT_DEST'), 215 | url=app.config.get('UPLOADS_DEFAULT_URL')) 216 | 217 | for uset in upload_sets: 218 | config = config_for_set(uset, app, defaults) 219 | set_config[uset.name] = config 220 | 221 | should_serve = any(s.base_url is None for s in set_config.values()) 222 | if '_uploads' not in app.blueprints and should_serve: 223 | app.register_blueprint(uploads_mod) 224 | 225 | 226 | class All(object): 227 | """ 228 | This type can be used to allow all extensions. There is a predefined 229 | instance named `ALL`. 230 | """ 231 | def __contains__(self, item): 232 | return True 233 | 234 | 235 | #: This "contains" all items. You can use it to allow all extensions to be 236 | #: uploaded. 237 | ALL = All() 238 | 239 | 240 | class AllExcept(object): 241 | """ 242 | This can be used to allow all file types except certain ones. For example, 243 | to ban .exe and .iso files, pass:: 244 | 245 | AllExcept(('exe', 'iso')) 246 | 247 | to the `UploadSet` constructor as `extensions`. You can use any container, 248 | for example:: 249 | 250 | AllExcept(SCRIPTS + EXECUTABLES) 251 | """ 252 | def __init__(self, items): 253 | self.items = items 254 | 255 | def __contains__(self, item): 256 | return item not in self.items 257 | 258 | 259 | class UploadConfiguration(object): 260 | """ 261 | This holds the configuration for a single `UploadSet`. The constructor's 262 | arguments are also the attributes. 263 | 264 | :param destination: The directory to save files to. 265 | :param base_url: The URL (ending with a /) that files can be downloaded 266 | from. If this is `None`, Flask-Uploads will serve the 267 | files itself. 268 | :param allow: A list of extensions to allow, even if they're not in the 269 | `UploadSet` extensions list. 270 | :param deny: A list of extensions to deny, even if they are in the 271 | `UploadSet` extensions list. 272 | """ 273 | def __init__(self, destination, base_url=None, allow=(), deny=()): 274 | self.destination = destination 275 | self.base_url = base_url 276 | self.allow = allow 277 | self.deny = deny 278 | 279 | @property 280 | def tuple(self): 281 | return (self.destination, self.base_url, self.allow, self.deny) 282 | 283 | def __eq__(self, other): 284 | return self.tuple == other.tuple 285 | 286 | 287 | class UploadSet(object): 288 | """ 289 | This represents a single set of uploaded files. Each upload set is 290 | independent of the others. This can be reused across multiple application 291 | instances, as all configuration is stored on the application object itself 292 | and found with `flask.current_app`. 293 | 294 | :param name: The name of this upload set. It defaults to ``files``, but 295 | you can pick any alphanumeric name you want. (For simplicity, 296 | it's best to use a plural noun.) 297 | :param extensions: The extensions to allow uploading in this set. The 298 | easiest way to do this is to add together the extension 299 | presets (for example, ``TEXT + DOCUMENTS + IMAGES``). 300 | It can be overridden by the configuration with the 301 | `UPLOADED_X_ALLOW` and `UPLOADED_X_DENY` configuration 302 | parameters. The default is `DEFAULTS`. 303 | :param default_dest: If given, this should be a callable. If you call it 304 | with the app, it should return the default upload 305 | destination path for that app. 306 | """ 307 | def __init__(self, name='files', extensions=DEFAULTS, default_dest=None): 308 | if not name.isalnum(): 309 | raise ValueError("Name must be alphanumeric (no underscores)") 310 | self.name = name 311 | self.extensions = extensions 312 | self._config = None 313 | self.default_dest = default_dest 314 | 315 | @property 316 | def config(self): 317 | """ 318 | This gets the current configuration. By default, it looks up the 319 | current application and gets the configuration from there. But if you 320 | don't want to go to the full effort of setting an application, or it's 321 | otherwise outside of a request context, set the `_config` attribute to 322 | an `UploadConfiguration` instance, then set it back to `None` when 323 | you're done. 324 | """ 325 | if self._config is not None: 326 | return self._config 327 | try: 328 | return current_app.upload_set_config[self.name] 329 | except AttributeError: 330 | raise RuntimeError("cannot access configuration outside request") 331 | 332 | def url(self, filename): 333 | """ 334 | This function gets the URL a file uploaded to this set would be 335 | accessed at. It doesn't check whether said file exists. 336 | 337 | :param filename: The filename to return the URL for. 338 | """ 339 | base = self.config.base_url 340 | if base is None: 341 | return url_for('_uploads.uploaded_file', setname=self.name, 342 | filename=filename, _external=True) 343 | else: 344 | return base + filename 345 | 346 | def path(self, filename, folder=None): 347 | """ 348 | This returns the absolute path of a file uploaded to this set. It 349 | doesn't actually check whether said file exists. 350 | 351 | :param filename: The filename to return the path for. 352 | :param folder: The subfolder within the upload set previously used 353 | to save to. 354 | """ 355 | if folder is not None: 356 | target_folder = os.path.join(self.config.destination, folder) 357 | else: 358 | target_folder = self.config.destination 359 | return os.path.join(target_folder, filename) 360 | 361 | def file_allowed(self, storage, basename): 362 | """ 363 | This tells whether a file is allowed. It should return `True` if the 364 | given `werkzeug.FileStorage` object can be saved with the given 365 | basename, and `False` if it can't. The default implementation just 366 | checks the extension, so you can override this if you want. 367 | 368 | :param storage: The `werkzeug.FileStorage` to check. 369 | :param basename: The basename it will be saved under. 370 | """ 371 | return self.extension_allowed(extension(basename)) 372 | 373 | def extension_allowed(self, ext): 374 | """ 375 | This determines whether a specific extension is allowed. It is called 376 | by `file_allowed`, so if you override that but still want to check 377 | extensions, call back into this. 378 | 379 | :param ext: The extension to check, without the dot. 380 | """ 381 | return ((ext in self.config.allow) or 382 | (ext in self.extensions and ext not in self.config.deny)) 383 | 384 | def get_basename(self, filename): 385 | return lowercase_ext(secure_filename(filename)) 386 | 387 | def save(self, storage, folder=None, name=None): 388 | """ 389 | This saves a `werkzeug.FileStorage` into this upload set. If the 390 | upload is not allowed, an `UploadNotAllowed` error will be raised. 391 | Otherwise, the file will be saved and its name (including the folder) 392 | will be returned. 393 | 394 | :param storage: The uploaded file to save. 395 | :param folder: The subfolder within the upload set to save to. 396 | :param name: The name to save the file as. If it ends with a dot, the 397 | file's extension will be appended to the end. (If you 398 | are using `name`, you can include the folder in the 399 | `name` instead of explicitly using `folder`, i.e. 400 | ``uset.save(file, name="someguy/photo_123.")`` 401 | """ 402 | if not isinstance(storage, FileStorage): 403 | raise TypeError("storage must be a werkzeug.FileStorage") 404 | 405 | if folder is None and name is not None and "/" in name: 406 | folder, name = os.path.split(name) 407 | 408 | basename = self.get_basename(storage.filename) 409 | if name: 410 | if name.endswith('.'): 411 | basename = name + extension(basename) 412 | else: 413 | basename = name 414 | 415 | if not self.file_allowed(storage, basename): 416 | raise UploadNotAllowed() 417 | 418 | if folder: 419 | target_folder = os.path.join(self.config.destination, folder) 420 | else: 421 | target_folder = self.config.destination 422 | if not os.path.exists(target_folder): 423 | os.makedirs(target_folder) 424 | if os.path.exists(os.path.join(target_folder, basename)): 425 | basename = self.resolve_conflict(target_folder, basename) 426 | 427 | target = os.path.join(target_folder, basename) 428 | storage.save(target) 429 | if folder: 430 | return posixpath.join(folder, basename) 431 | else: 432 | return basename 433 | 434 | def resolve_conflict(self, target_folder, basename): 435 | """ 436 | If a file with the selected name already exists in the target folder, 437 | this method is called to resolve the conflict. It should return a new 438 | basename for the file. 439 | 440 | The default implementation splits the name and extension and adds a 441 | suffix to the name consisting of an underscore and a number, and tries 442 | that until it finds one that doesn't exist. 443 | 444 | :param target_folder: The absolute path to the target. 445 | :param basename: The file's original basename. 446 | """ 447 | name, ext = os.path.splitext(basename) 448 | count = 0 449 | while True: 450 | count = count + 1 451 | newname = '%s_%d%s' % (name, count, ext) 452 | if not os.path.exists(os.path.join(target_folder, newname)): 453 | return newname 454 | 455 | 456 | uploads_mod = Blueprint('_uploads', __name__, url_prefix='/_uploads') 457 | 458 | 459 | @uploads_mod.route('//') 460 | def uploaded_file(setname, filename): 461 | config = current_app.upload_set_config.get(setname) 462 | if config is None: 463 | abort(404) 464 | return send_from_directory(config.destination, filename) 465 | 466 | 467 | class TestingFileStorage(FileStorage): 468 | """ 469 | This is a helper for testing upload behavior in your application. You 470 | can manually create it, and its save method is overloaded to set `saved` 471 | to the name of the file it was saved to. All of these parameters are 472 | optional, so only bother setting the ones relevant to your application. 473 | 474 | :param stream: A stream. The default is an empty stream. 475 | :param filename: The filename uploaded from the client. The default is the 476 | stream's name. 477 | :param name: The name of the form field it was loaded from. The default is 478 | `None`. 479 | :param content_type: The content type it was uploaded as. The default is 480 | ``application/octet-stream``. 481 | :param content_length: How long it is. The default is -1. 482 | :param headers: Multipart headers as a `werkzeug.Headers`. The default is 483 | `None`. 484 | """ 485 | def __init__(self, stream=None, filename=None, name=None, 486 | content_type='application/octet-stream', content_length=-1, 487 | headers=None): 488 | FileStorage.__init__(self, stream, filename, name=name, 489 | content_type=content_type, content_length=content_length, 490 | headers=None) 491 | self.saved = None 492 | 493 | def save(self, dst, buffer_size=16384): 494 | """ 495 | This marks the file as saved by setting the `saved` attribute to the 496 | name of the file it was saved to. 497 | 498 | :param dst: The file to save to. 499 | :param buffer_size: Ignored. 500 | """ 501 | if isinstance(dst, string_types): 502 | self.saved = dst 503 | else: 504 | self.saved = dst.name 505 | -------------------------------------------------------------------------------- /pythoncode/Flask/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, redirect, render_template, request, session, url_for, Response 2 | import cv2 3 | from pyzbar import pyzbar 4 | import json 5 | import time 6 | import os 7 | import numpy as np 8 | 9 | app = Flask(__name__, static_url_path='') 10 | 11 | 12 | 13 | 14 | 15 | 16 | #cascade to identify face and eyes 17 | face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml') 18 | eyes2 = cv2.CascadeClassifier('./haarcascades/haarcascade_eye.xml') 19 | 20 | 21 | 22 | print("Started") 23 | _SETTINGS={ 24 | 25 | 'camera1': {'source': -1, 'mode':0}, 26 | 'camera2': {'source': 2, 'mode':0} 27 | 28 | } 29 | 30 | _CAMERACHANGE = False 31 | _CAMERA1MODE = 0 32 | _CAMERA2MODE = 0 33 | _STATE={'status':'starting'} 34 | 35 | 36 | def loadSettings(): 37 | global _SETTINGS 38 | try: 39 | with open('settings.json') as json_file: 40 | _SETTINGS = json.load(json_file) 41 | except IOError: 42 | print("File not accessible") 43 | 44 | ########## 45 | loadSettings() 46 | # 47 | camera1 = cv2.VideoCapture(_SETTINGS['camera1']['source']) 48 | camera2 = cv2.VideoCapture(_SETTINGS['camera2']['source']) 49 | 50 | _CAMERA1MODE = 3 51 | _CAMERA2MODE = 0 52 | 53 | def RecreateCameras(): 54 | global camera1,camera2 55 | camera1 = cv2.VideoCapture(_SETTINGS['camera1']['source']) 56 | camera2 = cv2.VideoCapture(_SETTINGS['camera2']['source']) 57 | 58 | # rest API PART =========================================================================================== 59 | 60 | 61 | #save actual configuration to file 62 | @app.route('/savesettings',methods=['POST']) 63 | def savesettings(): 64 | info={} 65 | global _SETTINGS 66 | 67 | 68 | with open('settings.json', 'w') as outfile: 69 | json.dump(_SETTINGS, outfile) 70 | 71 | info['status']="Setting Saved " 72 | resp = json.dumps(info) 73 | return resp 74 | 75 | 76 | 77 | @app.route('/changemode',methods=['POST']) 78 | def changemode(): 79 | global _CAMERACHANGE, _CAMERA1MODE, _CAMERA2MODE 80 | global _SETTINGS 81 | info={} 82 | 83 | try: 84 | 85 | 86 | datar = str(request.data.decode('UTF-8')) 87 | print (datar) 88 | obj = json.loads(datar) 89 | cameranumber=obj['cameranumber'] 90 | mode=obj['cameramode'] 91 | 92 | info['status']="Success" 93 | 94 | if cameranumber==1: 95 | _SETTINGS['camera1']['mode']=mode 96 | _CAMERA1MODE = mode 97 | elif cameranumber==2: 98 | _SETTINGS['camera2']['mode']=mode 99 | _CAMERA2MODE == mode 100 | else: 101 | info['status']="Camera Not Found" 102 | 103 | print(_CAMERA1MODE) 104 | 105 | resp = json.dumps(info) 106 | return resp 107 | except: 108 | info['status']="Error " 109 | resp = json.dumps(info) 110 | return resp 111 | 112 | 113 | #save release cameras 114 | @app.route('/releasecameras',methods=['POST']) 115 | def releasecameras(): 116 | global camera1,camera2 117 | global _CAMERACHANGE 118 | _CAMERACHANGE=True 119 | camera1.release() 120 | camera2.release() 121 | #save release cameras 122 | info={} 123 | 124 | info['status']="CamerasReleased " 125 | resp = json.dumps(info) 126 | return resp 127 | 128 | 129 | @app.route('/restartcameras',methods=['POST']) 130 | def restartcameras(): 131 | global camera1,camera2 132 | info={} 133 | global _CAMERACHANGE 134 | camera1.release() 135 | camera2.release() 136 | 137 | RecreateCameras() 138 | _CAMERACHANGE=False 139 | 140 | info['status']="Cameras re engaged, refresh page " 141 | resp = json.dumps(info) 142 | return resp 143 | 144 | 145 | 146 | @app.route('/changecamera',methods=['POST']) 147 | def camerachange(): 148 | global camera1,camera2 149 | global _CAMERACHANGE 150 | global _SETTINGS 151 | info={} 152 | 153 | try: 154 | 155 | _CAMERACHANGE =True 156 | camera1.release() 157 | camera2.release() 158 | 159 | datar = str(request.data.decode('UTF-8')) 160 | print (datar) 161 | obj = json.loads(datar) 162 | cameranumber=obj['cameranumber'] 163 | camerasource=obj['camerasource'] 164 | 165 | info['status']="Success" 166 | 167 | if cameranumber==1: 168 | _SETTINGS['camera1']['source']=camerasource 169 | camera1 = cv2.VideoCapture(obj['camerasource']) 170 | RecreateCameras() 171 | 172 | elif cameranumber==2: 173 | _SETTINGS['camera2']['source']=camerasource 174 | camera2 == cv2.VideoCapture(obj['camerasource']) 175 | RecreateCameras() 176 | 177 | else: 178 | info['status']="Camera Not Found" 179 | 180 | 181 | _CAMERACHANGE=False 182 | resp = json.dumps(info) 183 | return resp 184 | except: 185 | info['status']="Error " 186 | _CAMERACHANGE=False 187 | resp = json.dumps(info) 188 | return resp 189 | 190 | #=======OPENCV FUNCTIONS :)========================================================================================== 191 | def faceDetection(image): 192 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 193 | # Detect the faces 194 | faces = face_cascade.detectMultiScale(gray, 1.1, 4) 195 | 196 | # Draw the rectangle around each face 197 | 198 | for (x,y,w,h) in faces: 199 | cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2) 200 | 201 | return image 202 | 203 | def barcodesDetection(image): 204 | barcodes = pyzbar.decode(image) 205 | for barcode in barcodes: 206 | # extract the bounding box location of the barcode and draw the 207 | # bounding box surrounding the barcode on the image 208 | (x, y, w, h) = barcode.rect 209 | cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) 210 | # the barcode data is a bytes object so if we want to draw it on 211 | # our output image we need to convert it to a string first 212 | barcodeData = barcode.data.decode("utf-8") 213 | barcodeType = barcode.type 214 | # draw the barcode data and barcode type on the image 215 | text = "{} ({})".format(barcodeData, barcodeType) 216 | cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 217 | 0.5, (0, 0, 255), 2) 218 | 219 | return image 220 | 221 | def circelesDetection(image): 222 | output = image.copy() 223 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 224 | # cv2.HoughCircles function has a lot of parameters, so you can find more about it in documentation 225 | # or you can use cv2.HoughCircles? in jupyter nootebook to get that 226 | 227 | # Check to see if there is any detection v2, 32.0, 30, 550 228 | #circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100) 229 | circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.6, 40) 230 | # ensure at least some circles were found 231 | if circles is not None: 232 | # convert the (x, y) coordinates and radius of the circles to integers 233 | circles = np.round(circles[0, :]).astype("int") 234 | # loop over the (x, y) coordinates and radius of the circles 235 | for (x, y, r) in circles: 236 | # draw the circle in the output image, then draw a rectangle 237 | # corresponding to the center of the circle 238 | cv2.circle(output, (x, y), r, (0, 255, 0), 4) 239 | cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1) 240 | # show the output image 241 | return output 242 | 243 | def leafstatus(image): 244 | 245 | hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) 246 | # find the green color 247 | mask_green = cv2.inRange(hsv, (36,0,0), (86,255,255)) 248 | # find the brown color 249 | mask_brown = cv2.inRange(hsv, (8, 60, 20), (30, 255, 200)) 250 | # find the yellow color in the leaf 251 | mask_yellow = cv2.inRange(hsv, (21, 39, 64), (40, 255, 255)) 252 | 253 | mask = cv2.bitwise_or(mask_brown, mask_yellow) 254 | mask = cv2.bitwise_or(mask, mask_green) 255 | 256 | resGreen = cv2.bitwise_and(image,image, mask= mask_green) 257 | resBrown = cv2.bitwise_and(image,image, mask= mask_brown) 258 | resYellow = cv2.bitwise_and(image,image, mask= mask_yellow) 259 | 260 | yellows='Yellow: '+ str(percentage(resYellow))+'%' 261 | browns='Brown: '+ str(percentage(resBrown))+'%' 262 | greens='Green: '+ str(percentage(resGreen))+'%' 263 | 264 | 265 | resYellow=writeOnImage(resYellow,yellows) 266 | resBrown=writeOnImage(resBrown,browns) 267 | resGreen=writeOnImage(resGreen,greens) 268 | img=writeOnImage(image,'Normal Image') 269 | 270 | #create overall thing 271 | vis = np.concatenate((img, resBrown), axis=0) 272 | vis1 = np.concatenate((resGreen, resYellow), axis=0) 273 | vistot = np.concatenate((vis, vis1), axis=1) 274 | 275 | return vistot 276 | 277 | def percentage(img): 278 | res1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 279 | nonZero = cv2.countNonZero(res1) 280 | percentage = (nonZero / (img.shape[0] * img.shape[1])) * 100 281 | percentage=int(percentage*10)/10 282 | return percentage 283 | 284 | def writeOnImage(img, text): 285 | font = cv2.FONT_HERSHEY_SIMPLEX 286 | # org 287 | org = (65, 65) 288 | # fontScale 289 | fontScale = 2 290 | # color in BGR 291 | color = (0, 255, 30) 292 | # Line thickness of 2 px 293 | thickness = 3 294 | # Using cv2.putText() method 295 | img = cv2.putText(img,text, org, font, 296 | fontScale, color, thickness, cv2.LINE_AA) 297 | 298 | return img 299 | 300 | #=======Frontend ========================================================================================= 301 | def selecMode(image, cameramode): 302 | 303 | if cameramode == 0: 304 | return image 305 | elif cameramode == 1: 306 | return circelesDetection(image) 307 | elif cameramode == 2: 308 | return faceDetection(image) 309 | elif cameramode == 3: 310 | return barcodesDetection(image) 311 | elif cameramode == 4: 312 | return leafstatus(image) 313 | else: 314 | return image 315 | 316 | 317 | def gen_frames1(): 318 | global camera1 # generate frame by frame from camera 319 | global _CAMERA1MODE 320 | while True: 321 | 322 | 323 | if not _CAMERACHANGE: 324 | success, frame = camera1.read() # read the camera frame 325 | if not success: 326 | break 327 | else: 328 | frame = selecMode(frame,_CAMERA1MODE) 329 | 330 | ret, buffer = cv2.imencode('.jpg', frame) 331 | 332 | frame = buffer.tobytes() 333 | yield (b'--frame\r\n' 334 | b'Content-Type: image/jpg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result 335 | 336 | 337 | @app.route('/video_feed1') 338 | def video_feed1(): 339 | #Video streaming route. Put this in the src attribute of an img tag 340 | return Response(gen_frames1(), mimetype='multipart/x-mixed-replace; boundary=frame') 341 | 342 | def gen_frames2(): 343 | global camera2 # generate frame by frame from camera 344 | global _CAMERA2MODE 345 | while True: 346 | 347 | # Capture frame-by-frame 348 | 349 | if not _CAMERACHANGE: 350 | success, frame = camera2.read() # read the camera frame 351 | if not success: 352 | break 353 | else: 354 | resize(frame, frame, Size(640, 360), 0, 0, INTER_CUBIC); 355 | 356 | frame = selecMode(frame,_CAMERA2MODE) 357 | ret, buffer = cv2.imencode('.jpg', frame) 358 | 359 | frame = buffer.tobytes() 360 | yield (b'--frame\r\n' 361 | b'Content-Type: image/jpg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result 362 | 363 | 364 | @app.route('/video_feed2') 365 | def video_feed2(): 366 | #Video streaming route. Put this in the src attribute of an img tag 367 | return Response(gen_frames2(), mimetype='multipart/x-mixed-replace; boundary=frame') 368 | 369 | 370 | 371 | @app.route('/') 372 | def index(): 373 | """Video streaming home page.""" 374 | return render_template('index.html') 375 | 376 | 377 | if __name__ == '__main__': 378 | app.run(host='0.0.0.0',debug = False, port=33443) -------------------------------------------------------------------------------- /pythoncode/Flask/settings.json: -------------------------------------------------------------------------------- 1 | {"camera1": {"source":-1 , "mode": 3}, "camera2": {"source": 2, "mode": 0}} -------------------------------------------------------------------------------- /pythoncode/Flask/static/assets/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mauringo/opencv-demo-webapp-snap/68b52b6930d1d31eed764d1e2a32a13073819d99/pythoncode/Flask/static/assets/Logo.png -------------------------------------------------------------------------------- /pythoncode/Flask/static/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mauringo/opencv-demo-webapp-snap/68b52b6930d1d31eed764d1e2a32a13073819d99/pythoncode/Flask/static/assets/icon.png -------------------------------------------------------------------------------- /pythoncode/Flask/static/css/style.css: -------------------------------------------------------------------------------- 1 | .vertical-spacing { 2 | margin-bottom: 2em; 3 | } 4 | 5 | .vertical-double-spacing { 6 | margin-top: 3em; 7 | } 8 | 9 | .bottom-spacing { 10 | margin-bottom: 1em; 11 | } 12 | 13 | .countour-spacing { 14 | margin-left: 2em; 15 | margin-right: 2em; 16 | } 17 | 18 | .logo { 19 | margin: 2em; 20 | } 21 | 22 | .code-text-area { 23 | 24 | height: 50em; 25 | } -------------------------------------------------------------------------------- /pythoncode/Flask/static/js/script.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | function setCameraMode(camerainput,modeinput){ 6 | 7 | var data = { cameramode: modeinput, cameranumber:camerainput }; 8 | 9 | fetch(location.origin+"/changemode", { 10 | method: 'POST', // or 'PUT' 11 | headers: { 12 | 'Content-Type': 'application/json', 13 | }, 14 | body: JSON.stringify(data), 15 | }) 16 | .then(response => response.json()) 17 | .then(data => { 18 | console.log('Success:', data); 19 | }) 20 | .catch((error) => { 21 | console.error('Error:', error); 22 | }); 23 | 24 | 25 | } 26 | 27 | 28 | function releaseCameras(){ 29 | 30 | var data = { }; 31 | 32 | fetch(location.origin+"/releasecameras", { 33 | method: 'POST', // or 'PUT' 34 | headers: { 35 | 'Content-Type': 'application/json', 36 | }, 37 | body: JSON.stringify(data), 38 | }) 39 | .then(response => response.json()) 40 | .then(data => { 41 | console.log('Success:', data); 42 | }) 43 | .catch((error) => { 44 | console.error('Error:', error); 45 | }); 46 | 47 | 48 | } 49 | 50 | 51 | function restartCameras(){ 52 | 53 | var data = { }; 54 | 55 | fetch(location.origin+"/restartcameras", { 56 | method: 'POST', // or 'PUT' 57 | headers: { 58 | 'Content-Type': 'application/json', 59 | }, 60 | body: JSON.stringify(data), 61 | }) 62 | .then(response => response.json()) 63 | .then(data => { 64 | console.log('Success:', data); 65 | }) 66 | .catch((error) => { 67 | console.error('Error:', error); 68 | }); 69 | 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /pythoncode/Flask/static/libs/bootstrap-4.4.1-dist/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | html { 15 | font-family: sans-serif; 16 | line-height: 1.15; 17 | -webkit-text-size-adjust: 100%; 18 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 19 | } 20 | 21 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { 22 | display: block; 23 | } 24 | 25 | body { 26 | margin: 0; 27 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 28 | font-size: 1rem; 29 | font-weight: 400; 30 | line-height: 1.5; 31 | color: #212529; 32 | text-align: left; 33 | background-color: #fff; 34 | } 35 | 36 | [tabindex="-1"]:focus:not(:focus-visible) { 37 | outline: 0 !important; 38 | } 39 | 40 | hr { 41 | box-sizing: content-box; 42 | height: 0; 43 | overflow: visible; 44 | } 45 | 46 | h1, h2, h3, h4, h5, h6 { 47 | margin-top: 0; 48 | margin-bottom: 0.5rem; 49 | } 50 | 51 | p { 52 | margin-top: 0; 53 | margin-bottom: 1rem; 54 | } 55 | 56 | abbr[title], 57 | abbr[data-original-title] { 58 | text-decoration: underline; 59 | -webkit-text-decoration: underline dotted; 60 | text-decoration: underline dotted; 61 | cursor: help; 62 | border-bottom: 0; 63 | -webkit-text-decoration-skip-ink: none; 64 | text-decoration-skip-ink: none; 65 | } 66 | 67 | address { 68 | margin-bottom: 1rem; 69 | font-style: normal; 70 | line-height: inherit; 71 | } 72 | 73 | ol, 74 | ul, 75 | dl { 76 | margin-top: 0; 77 | margin-bottom: 1rem; 78 | } 79 | 80 | ol ol, 81 | ul ul, 82 | ol ul, 83 | ul ol { 84 | margin-bottom: 0; 85 | } 86 | 87 | dt { 88 | font-weight: 700; 89 | } 90 | 91 | dd { 92 | margin-bottom: .5rem; 93 | margin-left: 0; 94 | } 95 | 96 | blockquote { 97 | margin: 0 0 1rem; 98 | } 99 | 100 | b, 101 | strong { 102 | font-weight: bolder; 103 | } 104 | 105 | small { 106 | font-size: 80%; 107 | } 108 | 109 | sub, 110 | sup { 111 | position: relative; 112 | font-size: 75%; 113 | line-height: 0; 114 | vertical-align: baseline; 115 | } 116 | 117 | sub { 118 | bottom: -.25em; 119 | } 120 | 121 | sup { 122 | top: -.5em; 123 | } 124 | 125 | a { 126 | color: #007bff; 127 | text-decoration: none; 128 | background-color: transparent; 129 | } 130 | 131 | a:hover { 132 | color: #0056b3; 133 | text-decoration: underline; 134 | } 135 | 136 | a:not([href]) { 137 | color: inherit; 138 | text-decoration: none; 139 | } 140 | 141 | a:not([href]):hover { 142 | color: inherit; 143 | text-decoration: none; 144 | } 145 | 146 | pre, 147 | code, 148 | kbd, 149 | samp { 150 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 151 | font-size: 1em; 152 | } 153 | 154 | pre { 155 | margin-top: 0; 156 | margin-bottom: 1rem; 157 | overflow: auto; 158 | } 159 | 160 | figure { 161 | margin: 0 0 1rem; 162 | } 163 | 164 | img { 165 | vertical-align: middle; 166 | border-style: none; 167 | } 168 | 169 | svg { 170 | overflow: hidden; 171 | vertical-align: middle; 172 | } 173 | 174 | table { 175 | border-collapse: collapse; 176 | } 177 | 178 | caption { 179 | padding-top: 0.75rem; 180 | padding-bottom: 0.75rem; 181 | color: #6c757d; 182 | text-align: left; 183 | caption-side: bottom; 184 | } 185 | 186 | th { 187 | text-align: inherit; 188 | } 189 | 190 | label { 191 | display: inline-block; 192 | margin-bottom: 0.5rem; 193 | } 194 | 195 | button { 196 | border-radius: 0; 197 | } 198 | 199 | button:focus { 200 | outline: 1px dotted; 201 | outline: 5px auto -webkit-focus-ring-color; 202 | } 203 | 204 | input, 205 | button, 206 | select, 207 | optgroup, 208 | textarea { 209 | margin: 0; 210 | font-family: inherit; 211 | font-size: inherit; 212 | line-height: inherit; 213 | } 214 | 215 | button, 216 | input { 217 | overflow: visible; 218 | } 219 | 220 | button, 221 | select { 222 | text-transform: none; 223 | } 224 | 225 | select { 226 | word-wrap: normal; 227 | } 228 | 229 | button, 230 | [type="button"], 231 | [type="reset"], 232 | [type="submit"] { 233 | -webkit-appearance: button; 234 | } 235 | 236 | button:not(:disabled), 237 | [type="button"]:not(:disabled), 238 | [type="reset"]:not(:disabled), 239 | [type="submit"]:not(:disabled) { 240 | cursor: pointer; 241 | } 242 | 243 | button::-moz-focus-inner, 244 | [type="button"]::-moz-focus-inner, 245 | [type="reset"]::-moz-focus-inner, 246 | [type="submit"]::-moz-focus-inner { 247 | padding: 0; 248 | border-style: none; 249 | } 250 | 251 | input[type="radio"], 252 | input[type="checkbox"] { 253 | box-sizing: border-box; 254 | padding: 0; 255 | } 256 | 257 | input[type="date"], 258 | input[type="time"], 259 | input[type="datetime-local"], 260 | input[type="month"] { 261 | -webkit-appearance: listbox; 262 | } 263 | 264 | textarea { 265 | overflow: auto; 266 | resize: vertical; 267 | } 268 | 269 | fieldset { 270 | min-width: 0; 271 | padding: 0; 272 | margin: 0; 273 | border: 0; 274 | } 275 | 276 | legend { 277 | display: block; 278 | width: 100%; 279 | max-width: 100%; 280 | padding: 0; 281 | margin-bottom: .5rem; 282 | font-size: 1.5rem; 283 | line-height: inherit; 284 | color: inherit; 285 | white-space: normal; 286 | } 287 | 288 | progress { 289 | vertical-align: baseline; 290 | } 291 | 292 | [type="number"]::-webkit-inner-spin-button, 293 | [type="number"]::-webkit-outer-spin-button { 294 | height: auto; 295 | } 296 | 297 | [type="search"] { 298 | outline-offset: -2px; 299 | -webkit-appearance: none; 300 | } 301 | 302 | [type="search"]::-webkit-search-decoration { 303 | -webkit-appearance: none; 304 | } 305 | 306 | ::-webkit-file-upload-button { 307 | font: inherit; 308 | -webkit-appearance: button; 309 | } 310 | 311 | output { 312 | display: inline-block; 313 | } 314 | 315 | summary { 316 | display: list-item; 317 | cursor: pointer; 318 | } 319 | 320 | template { 321 | display: none; 322 | } 323 | 324 | [hidden] { 325 | display: none !important; 326 | } 327 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /pythoncode/Flask/static/libs/bootstrap-4.4.1-dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /pythoncode/Flask/static/libs/bootstrap-4.4.1-dist/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/bootstrap-reboot.scss","../../scss/_reboot.scss","dist/css/bootstrap-reboot.css","../../scss/vendor/_rfs.scss","bootstrap-reboot.css","../../scss/mixins/_hover.scss"],"names":[],"mappings":"AAAA;;;;;;ACkBA,ECTA,QADA,SDaE,WAAA,WAGF,KACE,YAAA,WACA,YAAA,KACA,yBAAA,KACA,4BAAA,YAMF,QAAA,MAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,IAAA,QACE,QAAA,MAUF,KACE,OAAA,EACA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBEgFI,UAAA,KF9EJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,KACA,iBAAA,KGlBF,0CH+BE,QAAA,YASF,GACE,WAAA,YACA,OAAA,EACA,SAAA,QAaF,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KC9CF,0BDyDA,YAEE,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,cAAA,EACA,iCAAA,KAAA,yBAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QCnDF,GDsDA,GCvDA,GD0DE,WAAA,EACA,cAAA,KAGF,MCtDA,MACA,MAFA,MD2DE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAGF,ECvDA,ODyDE,YAAA,OAGF,MExFI,UAAA,IFiGJ,IC5DA,ID8DE,SAAA,SEnGE,UAAA,IFqGF,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAON,EACE,MAAA,QACA,gBAAA,KACA,iBAAA,YIhLA,QJmLE,MAAA,QACA,gBAAA,UASJ,cACE,MAAA,QACA,gBAAA,KI/LA,oBJkME,MAAA,QACA,gBAAA,KC7DJ,KACA,IDqEA,ICpEA,KDwEE,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UEpJE,UAAA,IFwJJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAEE,OAAA,EAAA,EAAA,KAQF,IACE,eAAA,OACA,aAAA,KAGF,IAGE,SAAA,OACA,eAAA,OAQF,MACE,gBAAA,SAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAGE,WAAA,QAQF,MAEE,QAAA,aACA,cAAA,MAMF,OAEE,cAAA,EAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBCxGF,OD2GA,MCzGA,SADA,OAEA,SD6GE,OAAA,EACA,YAAA,QErPE,UAAA,QFuPF,YAAA,QAGF,OC3GA,MD6GE,SAAA,QAGF,OC3GA,OD6GE,eAAA,KAMF,OACE,UAAA,OC3GF,cACA,aACA,cDgHA,OAIE,mBAAA,OC/GF,6BACA,4BACA,6BDkHE,sBAKI,OAAA,QClHN,gCACA,+BACA,gCDsHA,yBAIE,QAAA,EACA,aAAA,KCrHF,qBDwHA,kBAEE,WAAA,WACA,QAAA,EAIF,iBCxHA,2BACA,kBAFA,iBDkIE,mBAAA,QAGF,SACE,SAAA,KAEA,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAKF,OACE,QAAA,MACA,MAAA,KACA,UAAA,KACA,QAAA,EACA,cAAA,MEjSI,UAAA,OFmSJ,YAAA,QACA,MAAA,QACA,YAAA,OAGF,SACE,eAAA,SGvIF,yCFGA,yCD0IE,OAAA,KGxIF,cHgJE,eAAA,KACA,mBAAA,KG5IF,yCHoJE,mBAAA,KAQF,6BACE,KAAA,QACA,mBAAA,OAOF,OACE,QAAA,aAGF,QACE,QAAA,UACA,OAAA,QAGF,SACE,QAAA,KGzJF,SH+JE,QAAA","sourcesContent":["/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -webkit-tap-highlight-color: rgba($black, 0); // 5\n}\n\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\n// TODO: remove in v5\n// stylelint-disable-next-line selector-list-comma-newline-after\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != \"number\" or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1 / $rfs-rem-value);\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == \"px\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover() {\n &:hover { @content; }\n}\n\n@mixin hover-focus() {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus() {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active() {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]} -------------------------------------------------------------------------------- /pythoncode/Flask/static/libs/popper.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @popperjs/core v2.2.1 - MIT License 3 | */ 4 | 5 | "use strict";!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).Popper={})}(this,(function(e){function t(e){return{width:(e=e.getBoundingClientRect()).width,height:e.height,top:e.top,right:e.right,bottom:e.bottom,left:e.left,x:e.left,y:e.top}}function n(e){return"[object Window]"!==e.toString()?(e=e.ownerDocument)?e.defaultView:window:e}function r(e){return{scrollLeft:(e=n(e)).pageXOffset,scrollTop:e.pageYOffset}}function o(e){return e instanceof n(e).Element||e instanceof Element}function i(e){return e instanceof n(e).HTMLElement||e instanceof HTMLElement}function a(e){return e?(e.nodeName||"").toLowerCase():null}function s(e){return(o(e)?e.ownerDocument:e.document).documentElement}function f(e){return t(s(e)).left+r(e).scrollLeft}function p(e,o,p){void 0===p&&(p=!1),e=t(e);var c={scrollLeft:0,scrollTop:0},u={x:0,y:0};return p||("body"!==a(o)&&(c=o!==n(o)&&i(o)?{scrollLeft:o.scrollLeft,scrollTop:o.scrollTop}:r(o)),i(o)?((u=t(o)).x+=o.clientLeft,u.y+=o.clientTop):(o=s(o))&&(u.x=f(o))),{x:e.left+c.scrollLeft-u.x,y:e.top+c.scrollTop-u.y,width:e.width,height:e.height}}function c(e){return{x:e.offsetLeft,y:e.offsetTop,width:e.offsetWidth,height:e.offsetHeight}}function u(e){return"html"===a(e)?e:e.parentNode||e.host||document.ownerDocument||document.documentElement}function d(e){return n(e).getComputedStyle(e)}function l(e,t){void 0===t&&(t=[]);var r=function e(t){if(0<=["html","body","#document"].indexOf(a(t)))return t.ownerDocument.body;if(i(t)){var n=d(t);if(/auto|scroll|overlay|hidden/.test(n.overflow+n.overflowY+n.overflowX))return t}return e(u(t))}(e);return r=(e="body"===a(r))?n(r):r,t=t.concat(r),e?t:t.concat(l(u(r)))}function m(e){return i(e)&&"fixed"!==d(e).position?e.offsetParent:null}function h(e){var t=n(e);for(e=m(e);e&&0<=["table","td","th"].indexOf(a(e));)e=m(e);return e&&"body"===a(e)&&"static"===d(e).position?t:e||t}function v(e){var t=new Map,n=new Set,r=[];return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||function e(o){n.add(o.name),[].concat(o.requires||[],o.requiresIfExists||[]).forEach((function(r){n.has(r)||(r=t.get(r))&&e(r)})),r.push(o)}(e)})),r}function g(e){var t;return function(){return t||(t=new Promise((function(n){Promise.resolve().then((function(){t=void 0,n(e())}))}))),t}}function b(e){return e.split("-")[0]}function y(){for(var e=arguments.length,t=Array(e),n=0;n(g.devicePixelRatio||1)?"translate("+e+"px, "+u+"px)":"translate3d("+e+"px, "+u+"px, 0)",l)):Object.assign({},r,((t={})[v]=a?u+"px":"",t[m]=d?e+"px":"",t.transform="",t))}function j(e){return e.replace(/left|right|bottom|top/g,(function(e){return _[e]}))}function E(e){return e.replace(/start|end/g,(function(e){return U[e]}))}function D(e,t){var n=!(!t.getRootNode||!t.getRootNode().host);if(e.contains(t))return!0;if(n)do{if(t&&e.isSameNode(t))return!0;t=t.parentNode||t.host}while(t);return!1}function L(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function k(e,o){if("viewport"===o)e=L({width:(e=n(e)).innerWidth,height:e.innerHeight,x:0,y:0});else if(i(o))e=t(o);else{var a=s(e);e=n(a),o=r(a),(a=p(s(a),e)).height=Math.max(a.height,e.innerHeight),a.width=Math.max(a.width,e.innerWidth),a.x=-o.scrollLeft,a.y=-o.scrollTop,e=L(a)}return e}function P(e,t,r){return t="clippingParents"===t?function(e){var t=l(e),n=0<=["absolute","fixed"].indexOf(d(e).position)&&i(e)?h(e):e;return o(n)?t.filter((function(e){return o(e)&&D(e,n)})):[]}(e):[].concat(t),(r=(r=[].concat(t,[r])).reduce((function(t,r){var o=k(e,r),p=n(r=i(r)?r:s(e)),c=i(r)?d(r):{};parseFloat(c.borderTopWidth);var u=parseFloat(c.borderRightWidth)||0,l=parseFloat(c.borderBottomWidth)||0,m=parseFloat(c.borderLeftWidth)||0;c="html"===a(r);var h=f(r),v=r.clientWidth+u,g=r.clientHeight+l;return c&&50m?u:c?p.innerWidth-v-h:r.offsetWidth-v,p=c?p.innerHeight-g:r.offsetHeight-g,r=c?h:r.clientLeft,t.top=Math.max(o.top+l,t.top),t.right=Math.min(o.right-u,t.right),t.bottom=Math.min(o.bottom-p,t.bottom),t.left=Math.max(o.left+r,t.left),t}),k(e,r[0]))).width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}function B(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},{},e)}function W(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function H(e,n){void 0===n&&(n={});var r=n;n=void 0===(n=r.placement)?e.placement:n;var i=r.boundary,a=void 0===i?"clippingParents":i,f=void 0===(i=r.rootBoundary)?"viewport":i;i=void 0===(i=r.elementContext)?"popper":i;var p=r.altBoundary,c=void 0!==p&&p;r=B("number"!=typeof(r=void 0===(r=r.padding)?0:r)?r:W(r,q));var u=e.elements.reference;p=e.rects.popper,a=P(o(c=e.elements[c?"popper"===i?"reference":"popper":i])?c:c.contextElement||s(e.elements.popper),a,f),c=O({reference:f=t(u),element:p,strategy:"absolute",placement:n}),p=L(Object.assign({},p,{},c)),f="popper"===i?p:f;var d={top:a.top-f.top+r.top,bottom:f.bottom-a.bottom+r.bottom,left:a.left-f.left+r.left,right:f.right-a.right+r.right};if(e=e.modifiersData.offset,"popper"===i&&e){var l=e[n];Object.keys(d).forEach((function(e){var t=0<=["right","bottom"].indexOf(e)?1:-1,n=0<=["top","bottom"].indexOf(e)?"y":"x";d[e]+=l[n]*t}))}return d}function T(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function R(e){return["top","right","bottom","left"].some((function(t){return 0<=e[t]}))}var q=["top","bottom","right","left"],A=q.reduce((function(e,t){return e.concat([t+"-start",t+"-end"])}),[]),S=[].concat(q,["auto"]).reduce((function(e,t){return e.concat([t,t+"-start",t+"-end"])}),[]),C="beforeRead read afterRead beforeMain main afterMain beforeWrite write afterWrite".split(" "),F={placement:"bottom",modifiers:[],strategy:"absolute"},N={passive:!0},I={top:"auto",right:"auto",bottom:"auto",left:"auto"},_={left:"right",right:"left",bottom:"top",top:"bottom"},U={start:"end",end:"start"},V=[{name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(e){var t=e.state,r=e.instance,o=(e=e.options).scroll,i=void 0===o||o,a=void 0===(e=e.resize)||e,s=n(t.elements.popper),f=[].concat(t.scrollParents.reference,t.scrollParents.popper);return i&&f.forEach((function(e){e.addEventListener("scroll",r.update,N)})),a&&s.addEventListener("resize",r.update,N),function(){i&&f.forEach((function(e){e.removeEventListener("scroll",r.update,N)})),a&&s.removeEventListener("resize",r.update,N)}},data:{}},{name:"popperOffsets",enabled:!0,phase:"read",fn:function(e){var t=e.state;t.modifiersData[e.name]=O({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})},data:{}},{name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(e){var t=e.state,n=e.options;e=void 0===(e=n.gpuAcceleration)||e,n=void 0===(n=n.adaptive)||n,e={placement:b(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:e},t.styles.popper=Object.assign({},t.styles.popper,{},M(Object.assign({},e,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:n}))),null!=t.modifiersData.arrow&&(t.styles.arrow=Object.assign({},t.styles.arrow,{},M(Object.assign({},e,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})},data:{}},{name:"applyStyles",enabled:!0,phase:"write",fn:function(e){var t=e.state;Object.keys(t.elements).forEach((function(e){var n=t.styles[e]||{},r=t.attributes[e]||{},o=t.elements[e];i(o)&&a(o)&&(Object.assign(o.style,n),Object.keys(r).forEach((function(e){var t=r[e];!1===t?o.removeAttribute(e):o.setAttribute(e,!0===t?"":t)})))}))},effect:function(e){var t=e.state,n={popper:{position:"absolute",left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach((function(e){var r=t.elements[e],o=t.attributes[e]||{};e=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]).reduce((function(e,t){return e[t]="",e}),{}),i(r)&&a(r)&&(Object.assign(r.style,e),Object.keys(o).forEach((function(e){r.removeAttribute(e)})))}))}},requires:["computeStyles"]},{name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(e){var t=e.state,n=e.name,r=void 0===(e=e.options.offset)?[0,0]:e,o=(e=S.reduce((function(e,n){var o=t.rects,i=b(n),a=0<=["left","top"].indexOf(i)?-1:1,s="function"==typeof r?r(Object.assign({},o,{placement:n})):r;return o=(o=s[0])||0,s=((s=s[1])||0)*a,i=0<=["left","right"].indexOf(i)?{x:s,y:o}:{x:o,y:s},e[n]=i,e}),{}))[t.placement],i=o.y;t.modifiersData.popperOffsets.x+=o.x,t.modifiersData.popperOffsets.y+=i,t.modifiersData[n]=e}},{name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options;if(e=e.name,!t.modifiersData[e]._skip){var r=n.fallbackPlacements,o=n.padding,i=n.boundary,a=n.rootBoundary,s=n.altBoundary,f=void 0===(n=n.flipVariations)||n,p=b(n=t.options.placement);r=r||(p!==n&&f?function(e){if("auto"===b(e))return[];var t=j(e);return[E(e),t,E(t)]}(n):[j(n)]);var c=[n].concat(r).reduce((function(e,n){return e.concat("auto"===b(n)?function(e,t){void 0===t&&(t={});var n=t.boundary,r=t.rootBoundary,o=t.padding,i=t.flipVariations,a=t.placement.split("-")[1],s=(a?i?A:A.filter((function(e){return e.split("-")[1]===a})):q).reduce((function(t,i){return t[i]=H(e,{placement:i,boundary:n,rootBoundary:r,padding:o})[b(i)],t}),{});return Object.keys(s).sort((function(e,t){return s[e]-s[t]}))}(t,{placement:n,boundary:i,rootBoundary:a,padding:o,flipVariations:f}):n)}),[]);r=t.rects.reference,n=t.rects.popper;var u=new Map;p=!0;for(var d=c[0],l=0;ln[y]&&(v=j(v)),y=j(v),(h=[0>=x[h],0>=x[v],0>=x[y]]).every((function(e){return e}))){d=m,p=!1;break}u.set(m,h)}if(p)for(s=function(e){var t=c.find((function(t){if(t=u.get(t))return t.slice(0,e).every((function(e){return e}))}));if(t)return d=t,"break"},r=f?3:1;0 2 | 3 | 4 | 5 | 6 | 7 | 8 | OpenCV-Demo 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 | 46 |
47 |

After any camera restart reload the page.

48 |
49 | 50 |
51 |
52 | 53 | 54 | 55 |
56 |
57 |
58 | 59 | 60 |
61 |
62 |
63 |
64 | 65 | 66 | 67 | 68 |
69 |

Live Streaming

70 | Responsive image 71 |
72 | 73 |
74 |
75 | 76 | 77 | 78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 86 | 87 |
88 |
89 | 90 | 91 | 92 | 93 | 94 |
95 |
96 | 97 |
98 |
99 | 100 | 101 | 102 |
103 |
104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /shscripts/desktop-launch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | xdg-open http://localhost:33443 3 | -------------------------------------------------------------------------------- /shscripts/info.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "The Snap configuration files are stored in:" 4 | echo $SNAP_DATA 5 | 6 | echo $HOME 7 | echo $USER 8 | env 9 | 10 | path="/home/$(ls /home)" 11 | echo $path 12 | exit 0 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /shscripts/runserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | 5 | 6 | 7 | cd $SNAP_DATA/Flask 8 | python3 app.py 9 | 10 | 11 | 12 | exit 1 13 | 14 | 15 | -------------------------------------------------------------------------------- /snap/gui/app.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Encoding=UTF-8 4 | Name=OpenCV-Demo-Webapp 5 | Comment=just an Opencv SNAP Example 6 | Exec=opencv-demo-webapp.desktop-launch 7 | Icon=${SNAP}/meta/gui/icon.png 8 | Terminal=false 9 | Categories=Development; 10 | -------------------------------------------------------------------------------- /snap/gui/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mauringo/opencv-demo-webapp-snap/68b52b6930d1d31eed764d1e2a32a13073819d99/snap/gui/icon.png -------------------------------------------------------------------------------- /snap/hooks/install: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | 4 | #this script runs once at the installation. place your code below 5 | echo "You are installing the app" 6 | 7 | cp -a $SNAP/bin/pythoncode/* $SNAP_DATA/ 8 | 9 | 10 | echo "hello" > "$SNAP_DATA/hello.conf" 11 | -------------------------------------------------------------------------------- /snap/hooks/remove: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | #this script runs once at the removing. place your code below 4 | echo "You are removing the app" 5 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: opencv-demo-webapp 2 | base: core18 3 | version: '0.1' 4 | summary: OpenCV webAPP example, python based. active on port 33443. 5 | description: | 6 | Install the app and navigate to http://yourDeviceIp:33443. The app uses your webcam to 7 | implement some simple image vision algorithm, the result is then streamed inside a webpage. 8 | 9 | confinement: strict 10 | grade: stable 11 | 12 | apps: 13 | 14 | 15 | flask-server: 16 | command: sh $SNAP/bin/runserver.sh 17 | plugs: [network, network-bind, mount-observe, network-observe, camera, system-observe, bluetooth-control, joystick, x11] 18 | daemon: simple 19 | environment: 20 | "LD_LIBRARY_PATH": "$LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/lapack:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/blas" 21 | desktop-launch: 22 | command: bin/desktop-launch 23 | plugs: 24 | - desktop 25 | 26 | info: 27 | command: sh $SNAP/bin/info.sh 28 | plugs: [network, network-bind, mount-observe, network-observe, system-observe, bluetooth-control] 29 | 30 | 31 | 32 | parts: 33 | 34 | requests: 35 | plugin: python 36 | python-version: python3 37 | python-packages: [requests] 38 | build-packages: 39 | - python3-dev 40 | - build-essential 41 | 42 | ai: 43 | plugin: nil 44 | stage-packages: 45 | - python3-numpy 46 | - libzbar0 47 | - libzbar-dev 48 | - python3-opencv 49 | - python3-matplotlib 50 | - python3-pandas 51 | 52 | barcodes: 53 | plugin: python 54 | python-version: python3 55 | python-packages: [pyzbar] 56 | build-packages: 57 | - python3-dev 58 | - build-essential 59 | 60 | 61 | 62 | flask: 63 | plugin: python 64 | python-version: python3 65 | python-packages: [flask] 66 | build-packages: 67 | - python3-dev 68 | - build-essential 69 | 70 | 71 | 72 | 73 | python-libs: 74 | source: ./lib/ 75 | plugin: dump 76 | organize: 77 | '*': /lib/python3.6/site-packages/ 78 | 79 | python-code: 80 | source: ./pythoncode/ 81 | plugin: dump 82 | organize: 83 | '*': bin/pythoncode/ 84 | 85 | shscripts: 86 | source: ./shscripts/ 87 | plugin: dump 88 | organize: 89 | '*': bin/ 90 | 91 | 92 | --------------------------------------------------------------------------------