├── .gitignore ├── src └── Jasekz │ └── Laradrop │ ├── Events │ ├── Event.php │ ├── FileWasDeleted.php │ └── FileWasUploaded.php │ ├── resources │ ├── assets │ │ ├── img │ │ │ └── genericThumbs │ │ │ │ ├── folder.png │ │ │ │ └── no-thumb.png │ │ ├── css │ │ │ └── styles.css │ │ └── js │ │ │ ├── laradrop.js │ │ │ └── enyo.dropzone.js │ ├── lang │ │ └── en │ │ │ ├── err.php │ │ │ └── app.php │ └── views │ │ ├── mainContainer.blade.php │ │ ├── previewContainer.blade.php │ │ └── fileContainer.blade.php │ ├── LaradropFacade.php │ ├── Handlers │ └── Events │ │ └── DeleteFile.php │ ├── Http │ ├── routes.php │ └── Controllers │ │ └── LaradropController.php │ ├── database │ └── migrations │ │ ├── 2020_08_29_000000_modify_laradrop_files_table_add_soft_deletes.php │ │ ├── 2015_08_09_000000_create_laradrop_files_table.php │ │ └── 2016_02_25_000000_modify_laradrop_files_table_add_nesting.php │ ├── config │ └── config.php │ ├── Models │ └── File.php │ ├── LaradropServiceProvider.php │ └── Services │ └── File.php ├── .editorconfig ├── LICENSE ├── composer.json ├── tests └── LaradropControllerCRUDTest.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | composer.lock -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Events/Event.php: -------------------------------------------------------------------------------- 1 | 'File not provided.', 17 | ]; 18 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Events/FileWasDeleted.php: -------------------------------------------------------------------------------- 1 | data = $data; 21 | } 22 | 23 | /** 24 | * Get the channels the event should be broadcast on. 25 | * 26 | * @return array 27 | */ 28 | public function broadcastOn() 29 | { 30 | return []; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Events/FileWasUploaded.php: -------------------------------------------------------------------------------- 1 | data = $data; 21 | } 22 | 23 | /** 24 | * Get the channels the event should be broadcast on. 25 | * 26 | * @return array 27 | */ 28 | public function broadcastOn() 29 | { 30 | return []; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/views/mainContainer.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 | 10 |
11 |
12 |
-------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/views/previewContainer.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
4 |
5 |
6 |
7 |
8 |

9 | 10 |
11 |
12 |

13 |
14 | 15 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/lang/en/app.php: -------------------------------------------------------------------------------- 1 | '+ Add Folder', 17 | 'delete' => '✘ delete', 18 | 'move' => '↷ move', 19 | 'save' => 'Save', 20 | 'select' => '✔ select', 21 | 'startUpload' => 'Start Upload', 22 | 'uploadFiles' => '⇪ Click or drop files here to upload.', 23 | ]; 24 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Handlers/Events/DeleteFile.php: -------------------------------------------------------------------------------- 1 | data['file']->meta); 25 | $disk = Storage::disk($meta->disk); 26 | $disk->delete($event->data['file']->filename); 27 | $disk->delete('_thumb_' . $event->data['file']->filename); 28 | } catch (Exception $e) { 29 | throw $e; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Http/routes.php: -------------------------------------------------------------------------------- 1 | config('laradrop.middleware') ? config('laradrop.middleware') : null], function () { 6 | 7 | Route::get('laradrop/containers', [ 8 | 'as' => 'laradrop.containers', 9 | 'uses' => '\Jasekz\Laradrop\Http\Controllers\LaradropController@getContainers', 10 | ]); 11 | 12 | Route::post('laradrop/move', [ 13 | 'as' => 'laradrop.move', 14 | 'uses' => '\Jasekz\Laradrop\Http\Controllers\LaradropController@move', 15 | ]); 16 | 17 | Route::post('laradrop/create', [ 18 | 'as' => 'laradrop.create', 19 | 'uses' => '\Jasekz\Laradrop\Http\Controllers\LaradropController@create', 20 | ]); 21 | 22 | Route::resource('laradrop', '\Jasekz\Laradrop\Http\Controllers\LaradropController')->except(['create']); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/database/migrations/2020_08_29_000000_modify_laradrop_files_table_add_soft_deletes.php: -------------------------------------------------------------------------------- 1 | softDeletes(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table('laradrop_files', function (Blueprint $table) { 30 | $table->dropSoftDeletes(); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/database/migrations/2015_08_09_000000_create_laradrop_files_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 20 | $table->string('filename')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('laradrop_files'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/config/config.php: -------------------------------------------------------------------------------- 1 | env('LARADROP_MAX_UPLOAD_SIZE', 10), 6 | 7 | // max size for thumb generation in MB; if your server crashes while trying to generate thumbs, you can lower the threshold here 8 | 'max_thumbnail_size' => env('LARADROP_MAX_THUMBNAIL_SIZE', 10), 9 | 10 | // dimensions for thumbnail generator 11 | 'thumb_dimensions' => ['width' => env('LARADROP_THUMB_WIDTH', 150), 'height' => env('LARADROP_THUMB_HEIGHT', 150)], 12 | 13 | // default thumbnail (if one can not be generated) 14 | 'default_thumbnail_url' => env('LARADROP_DEFAULT_THUMB', '/vendor/jasekz/laradrop/img/genericThumbs/no-thumb.png'), 15 | 16 | // storage location - use config/filesystems.php 'disks' 17 | 'disk' => env('LARADROP_DISK', 'local'), 18 | 19 | // if this needs to be publicly accessible, this is the 'root storage directory' 20 | 'disk_public_url' => env('LARADROP_DISK_PUBLIC_URL', ''), 21 | ]; 22 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Models/File.php: -------------------------------------------------------------------------------- 1 | hasMany('Jasekz\Laradrop\Models\File', 'parent_id'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/views/fileContainer.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

[[alias]]

4 |

[[type]] / [[updated_at]]

5 |

6 | {{ trans('laradrop::app.select') }} 8 | {{ trans('laradrop::app.delete') }} 10 | {{ trans('laradrop::app.move') }} 12 |

13 | [[alias]] 14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | .laradrop .progress { 2 | height:5px; 3 | } 4 | 5 | .laradrop-folder{ 6 | background:#999; 7 | } 8 | 9 | .laradrop-folder img{ 10 | cursor:pointer; 11 | } 12 | 13 | .laradrop-droppable-highlight { 14 | background:#F7ED9C; 15 | } 16 | 17 | .laradrop-droppable-hover { 18 | background:#73A268; 19 | } 20 | 21 | .laradrop-thumbnail .move { 22 | cursor:move; 23 | } 24 | 25 | .laradrop .text-info, .laradrop h4 { 26 | font-size:10px; 27 | } 28 | 29 | .laradrop-breadcrumbs-container { 30 | position: relative; 31 | top: 10px; 32 | } 33 | 34 | .laradrop-breadcrumbs { 35 | padding:5px; 36 | font-size:18px; 37 | } 38 | 39 | .laradrop-breadcrumbs .arrow { 40 | padding: 0 10px 0 15px; 41 | } 42 | 43 | .laradrop-thumbnail .well { 44 | background-color:#FFFCFC; 45 | height:300px; 46 | } 47 | 48 | .laradrop-container button { 49 | padding:10px; 50 | font-size:18px; 51 | } 52 | 53 | .laradrop-previews .dz-image-preview { 54 | margin-bottom:15px; 55 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jasekz/laradrop", 3 | "description": "File manager using Dropzone.js for Laravel 5 | 6 | 7 | 8", 4 | "keywords": [ 5 | "php", 6 | "laravel 5", 7 | "laravel 6", 8 | "laravel 7", 9 | "laravel 8", 10 | "dropzone.js", 11 | "file manager", 12 | "file upload" 13 | ], 14 | "type": "library", 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Jasek Zbigniew", 19 | "email": "zig@elegrit.com", 20 | "homepage": "http://elegrit.com" 21 | } 22 | ], 23 | "require": { 24 | "php": ">=5.4.0", 25 | "illuminate/support": "~5.0|^6.0|^7.0|^8.0", 26 | "ext-fileinfo": "*", 27 | "intervention/image": "2.*", 28 | "illuminate/database": "~5.0|^6.0|^7.0|^8.0", 29 | "ext-json": "*", 30 | "illuminate/routing": "~5.0|^6.0|^7.0|^8.0", 31 | "illuminate/queue": "~5.0|^6.0|^7.0|^8.0" 32 | }, 33 | "autoload": { 34 | "psr-0": { 35 | "Jasekz\\Laradrop": "src" 36 | } 37 | }, 38 | "extra": { 39 | "laravel": { 40 | "providers": [ 41 | "Jasekz\\Laradrop\\LaradropServiceProvider" 42 | ] 43 | } 44 | }, 45 | "minimum-stability": "dev", 46 | "prefer-stable": true 47 | } 48 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/database/migrations/2016_02_25_000000_modify_laradrop_files_table_add_nesting.php: -------------------------------------------------------------------------------- 1 | integer('parent_id')->nullable()->index()->after('id'); 19 | $table->integer('lft')->nullable()->index()->after('parent_id'); 20 | $table->integer('rgt')->nullable()->index()->after('lft'); 21 | $table->integer('depth')->nullable()->after('rgt'); 22 | $table->string('type')->nullable()->after('depth'); 23 | $table->text('meta')->nullable()->after('type'); 24 | $table->string('public_resource_url')->nullable()->after('filename'); 25 | $table->string('alias')->nullable()->after('filename'); 26 | $table->smallInteger('has_thumbnail')->after('type')->default(0); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::table('laradrop_files', function (Blueprint $table) { 38 | $table->dropColumn('parent_id'); 39 | $table->dropColumn('lft'); 40 | $table->dropColumn('rgt'); 41 | $table->dropColumn('depth'); 42 | $table->dropColumn('type'); 43 | $table->dropColumn('meta'); 44 | $table->dropColumn('public_resource_url'); 45 | $table->dropColumn('alias'); 46 | $table->dropColumn('has_thumbnail'); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/LaradropServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'Jasekz\Laradrop\Handlers\Events\DeleteFile', 19 | ], 20 | ]; 21 | 22 | /** 23 | * Bootstrap the application services. 24 | * 25 | * @param DispatcherContract|null $events 26 | * @return void 27 | */ 28 | public function boot(DispatcherContract $events = null) 29 | { 30 | // different constructor signature for 5.3+ 31 | if (\App::version() >= '5.3.0') { 32 | parent::boot(); 33 | } else { 34 | parent::boot($events); 35 | } 36 | 37 | if (!$this->app->routesAreCached()) { 38 | require __DIR__ . '/Http/routes.php'; 39 | } 40 | 41 | $this->publishes([ 42 | __DIR__ . '/config/config.php' => config_path('laradrop.php') 43 | ]); 44 | 45 | $this->publishes([ 46 | __DIR__ . '/database/migrations/' => database_path('migrations') 47 | ], 'migrations'); 48 | 49 | $this->publishes([ 50 | __DIR__ . '/resources/assets' => public_path('vendor/jasekz/laradrop') 51 | ], 'public'); 52 | 53 | $this->loadViewsFrom(__DIR__ . '/resources/views', 'laradrop'); 54 | 55 | $this->loadTranslationsFrom(__DIR__ . '/resources/lang', 'laradrop'); 56 | } 57 | 58 | /** 59 | * Register the application services. 60 | * 61 | * @return void 62 | */ 63 | public function register() 64 | { 65 | $this->app->bind('laradrop', function($app) { 66 | return new LaradropFileService(); 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Services/File.php: -------------------------------------------------------------------------------- 1 | count() && $parentId > 0) { 24 | $files = $this->where('parent_id', '=', $parentId)->get(); 25 | } else if ($this->count()) { 26 | $files = $this->whereNull('parent_id')->get(); 27 | } 28 | 29 | if (isset($files)) { 30 | 31 | foreach ($files as $file) { 32 | 33 | if ($file->has_thumbnail && config('laradrop.disk_public_url')) { 34 | 35 | $publicResourceUrlSegments = explode('/', $file->public_resource_url); 36 | $publicResourceUrlSegments[count($publicResourceUrlSegments) - 1] = '_thumb_' . $publicResourceUrlSegments[count($publicResourceUrlSegments) - 1]; 37 | $file->filename = implode('/', $publicResourceUrlSegments); 38 | 39 | } else { 40 | $file->filename = config('laradrop.default_thumbnail_url'); 41 | } 42 | 43 | $file->numChildren = $file->children()->count(); 44 | 45 | if ($file->type == 'folder') { 46 | array_unshift($out, $file); 47 | } else { 48 | $out[] = $file; 49 | } 50 | } 51 | } 52 | 53 | return $out; 54 | } catch (\Exception $e) { 55 | throw $e; 56 | } 57 | } 58 | 59 | /** 60 | * Delete file(s) 61 | * 62 | * @param int $id 63 | * @return int|void 64 | * @throws \Exception 65 | */ 66 | public static function destroy($id) 67 | { 68 | try { 69 | $file = self::find($id); 70 | 71 | if ($file->children()->exists()) { 72 | foreach ($file->children()->where('type', '!=', 'folder')->get() as $descendant) { 73 | 74 | event(new FileWasDeleted([ // fire 'file deleted' event for each descendant 75 | 'file' => $descendant 76 | ])); 77 | 78 | $descendant->delete(); 79 | } 80 | } 81 | 82 | $file->delete($id); 83 | 84 | if ($file->type != 'folder') { 85 | event(new FileWasDeleted([ // fire 'file deleted' event for file 86 | 'file' => $file 87 | ])); 88 | } 89 | } catch (\Exception $e) { 90 | throw $e; 91 | } 92 | } 93 | 94 | /** 95 | * Move file 96 | * 97 | * @param $draggedFileId 98 | * @param $droppedFileId 99 | * @throws \Exception 100 | */ 101 | public function move($draggedFileId, $droppedFileId) 102 | { 103 | try { 104 | $dragged = $this->find($draggedFileId); 105 | $dropped = $this->find($droppedFileId); 106 | 107 | if ($droppedFileId == 0) { 108 | $dragged->parent_id = null; 109 | } else { 110 | $dragged->parent_id = $dropped->id; 111 | } 112 | $dragged->save(); 113 | } catch (\Exception $e) { 114 | throw $e; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tests/LaradropControllerCRUDTest.php: -------------------------------------------------------------------------------- 1 | withoutEvents(); 38 | 39 | $this->fileName = 'laradrop-test.png'; 40 | 41 | $this->storage = Storage::fake('laradrop-tests'); 42 | config(['laradrop.disk' => 'laradrop-tests']); 43 | 44 | $this->file = UploadedFile::fake()->image($this->fileName); 45 | $this->fileService = new FileService(); 46 | 47 | $this->controller = new LaradropController($this->fileService); 48 | 49 | $this->newFileFromDb = new FileService(); 50 | $this->newFile = new FileService(); 51 | } 52 | 53 | /** @test */ 54 | public function throws_error_if_uploaded_file_is_missing() 55 | { 56 | $res = $this->controller->store(Request::create('/store', 'POST')); 57 | 58 | $this->assertEquals('err.fileNotProvided', $res->getData()->msg); 59 | } 60 | 61 | /** @test */ 62 | public function successfully_uploads_file() 63 | { 64 | $this->setFiles(); 65 | 66 | $this->storage->assertExists($this->newFile->filename); 67 | $this->assertEquals($this->newFile->id, $this->newFileFromDb->id); 68 | } 69 | 70 | /** @test */ 71 | public function edits_filename() 72 | { 73 | $this->setFiles(); 74 | 75 | $this->assertEquals($this->newFile->filename, $this->newFileFromDb->filename); 76 | 77 | $newFileName = '_prefix_' . $this->newFile->filename; 78 | $this->controller->update( Request::create('/update', 'POST', ['filename' => $newFileName]), $this->newFile->id ); 79 | $this->newFileFromDb = FileService::find($this->newFile->id); 80 | 81 | $this->assertNotEquals($this->newFile->filename, $this->newFileFromDb->filename); 82 | $this->assertEquals($this->newFileFromDb->filename, $newFileName); 83 | } 84 | 85 | /** @test */ 86 | public function deletes_file() 87 | { 88 | $this->setFiles(); 89 | 90 | $this->assertEquals($this->newFile->filename, $this->newFileFromDb->filename); 91 | 92 | $fileId = $this->newFile->id; 93 | $this->expectsEvents(FileWasDeleted::class); 94 | $this->controller->destroy( $fileId ); 95 | $file = FileService::find($fileId); 96 | 97 | $this->assertNull($file); 98 | } 99 | 100 | /** @test */ 101 | public function creates_folder_with_filename_provided() 102 | { 103 | $res = $this->controller->create( Request::create('/update', 'POST', ['filename' => $this->fileName, 'type' => 'folder']) ); 104 | $fileFromDb = FileService::find($res->getData()->id); 105 | 106 | $this->assertEquals('success', $res->getData()->status); 107 | $this->assertEquals($res->getData()->id, $fileFromDb->id); 108 | $this->assertEquals('folder', $fileFromDb->type); 109 | $this->assertEquals($this->fileName, $fileFromDb->alias); 110 | } 111 | 112 | /** @test */ 113 | public function creates_folder_with_filename_not_provided() 114 | { 115 | $res = $this->controller->create( Request::create('/update', 'POST', ['type' => 'folder']) ); 116 | $fileFromDb = FileService::find($res->getData()->id); 117 | 118 | $this->assertEquals('success', $res->getData()->status); 119 | $this->assertEquals($res->getData()->id, $fileFromDb->id); 120 | $this->assertEquals('folder', $fileFromDb->type); 121 | $this->assertNotEquals($this->fileName, $fileFromDb->alias); 122 | } 123 | 124 | /** @test */ 125 | public function moves_file() 126 | { 127 | $this->setFiles(); 128 | 129 | $res = $this->controller->create( Request::create('/create', 'POST', ['filename' => 'Folder 1', 'type' => 'folder']) ); 130 | $draggedInId = $this->newFile->id; 131 | $droppedInId = $res->getData()->id; 132 | $res = $this->controller->move( Request::create('/move', 'POST', ['draggedId' => $draggedInId, 'droppedId' => $droppedInId]) ); 133 | $draggedIn = FileService::find($draggedInId); 134 | $droppedIn = FileService::find($droppedInId); 135 | 136 | $this->assertEquals($draggedIn->parent_id, $droppedIn->id); 137 | $this->assertEquals('success', $res->getData()->status); 138 | } 139 | 140 | protected function setFiles() 141 | { 142 | if( ! $this->newFile->id ) $this->newFile = $this->controller->store( Request::create('/store', 'POST', [], [], ['file' => $this->file]) ); 143 | if( ! $this->newFileFromDb->id ) $this->newFileFromDb = FileService::find($this->newFile->id); 144 | } 145 | 146 | protected function tearDown() : void 147 | { 148 | parent::tearDown(); 149 | 150 | $this->storage->delete([$this->newFile->filename, '_thumb_' . $this->newFile->filename]); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *This uses SoftDelete* 2 | # Laradrop 3 | 4 | [![Software License][ico-license]](LICENSE.md) 5 | 6 | 7 | This is a file manager using Dropzone.js for Laravel 5, 6, 7, 8. It provides basic functionality for managing, uploading, 8 | and deleting files. 9 | 10 | ## Quick Start 11 | 12 | 1) Follow the **Installation** instructions below. 13 | 14 | Getting errors? Make sure you have a database set up (https://laravel.com/docs/database). 15 | 16 | 2) In a view (welcome.blade.php, for example), add: 17 | ```html 18 | 19 | 20 | Laradrop Demo 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 37 | 38 | ``` 39 | 40 | 3) In your **.env** file, add: 41 | 42 | ``` 43 | LARADROP_DISK_PUBLIC_URL=/uploads 44 | LARADROP_DISK=laradrop 45 | ``` 46 | 4) In your **config/filesystems.php**, add to your `disks` array: 47 | ``` 48 | 'laradrop' => [ 49 | 'driver' => 'local', 50 | 'root' => public_path('uploads'), // will put files in 'public/upload' directory 51 | ], 52 | ``` 53 | That's it. If you have any issues or question, please feel free to open an issue. 54 | 55 | ## Installation 56 | 57 | NOTE: If you haven't set up a database yet for your app, please do that first as per Laravel docs - http://laravel.com/docs/database. 58 | 59 | Via composer 60 | ``` 61 | composer require jasekz/laradrop 62 | ``` 63 | 64 | Then in your `config/app.php` add 65 | ```php 66 | 'Jasekz\Laradrop\LaradropServiceProvider' 67 | ``` 68 | to the `providers` array. 69 | 70 | Then run 71 | 72 | php artisan vendor:publish 73 | 74 | followed by 75 | 76 | php artisan migrate 77 | 78 | ## Configuration (.env) 79 | 80 | Laradrop uses Laravel's Filesystem mechanism (https://laravel.com/docs/filesystem) and by default will store your 81 | files in the `storage/app` directory. If you would like to modify this behavior, along with other default settings, you can set your `.env` file variables: 82 | ```php 83 | 84 | # s3, local, or Rackspace. See 'Other Driver Prerequisites' at https://laravel.com/docs/filesystem. Defaults to 'local' 85 | LARADROP_DISK=local 86 | 87 | # If your files need to be web accessible, set this param. S3, for example, would be 'https://s3.amazonaws.com/my-bucket'. Defaults to the web root (public). 88 | LARADROP_DISK_PUBLIC_URL=/img 89 | 90 | # If a thumbnail can not be generated due to incompatible file or any other reason, what image do you want to use? Defaults to 'vendor/jasekz/laradrop/img/genericThumbs/no-thumb.png' 91 | LARADROP_DEFAULT_THUMB=/img/no-thumb.png 92 | 93 | # Max file upload size in MB. Defaults to 10. 94 | LARADROP_MAX_UPLOAD_SIZE=20 95 | 96 | # Max file size (in MB) for which thumbnail generation will be attempted. If your server has an issue processing thumbs, you can lower this value. Defaults to 10. 97 | LARADROP_MAX_THUMBNAIL_SIZE=10 98 | 99 | # Defaults to 150px. 100 | LARADROP_THUMB_WIDTH=150 101 | 102 | # Defaults to 150px. 103 | LARADROP_THUMB_HEIGHT=150 104 | 105 | # Run laradrop routes through middlware. Defaults to none. 106 | LARADROP_MIDDLEWARE=web 107 | ``` 108 | ## Usage 109 | This package requires Dropzone.js, jQuery, and jQuery UI. Include these somewhere in your template: 110 | ``` php 111 | 112 | 113 | 114 | 115 | ``` 116 | 117 | By default, Laradrop is designed for Bootstrap, but it's not a requirement. Include Bootstrap and the Laradrop styles if you'd like to use it: 118 | ``` php 119 | 120 | 121 | ``` 122 | 123 | 124 | Add the html code where you'd like to implement the file manager. Note, that by default, there is no middleware assigned to the Laradrop controller, however, it you assign middleware which contains csrf protection, you must include the `laradrop-csrf-token="{{ csrf_token() }}"` attribute. 125 | ``` html 126 |
127 | ``` 128 | 129 | Finally, bind it using jQuery: 130 | ```javascript 131 | 154 | ``` 155 | 156 | ## Events 157 | Laradrop currently fires two events: 158 | 159 | 1. **Jasekz\Laradrop\Events\FileWasUploaded** - this is fired after a file has been uploaded and saved. 160 | 2. **Jasekz\Laradrop\Events\FileWasDeleted** - this is fired after a file is deleted. 161 | 162 | ## Handlers (upload, delete, list, etc) 163 | If you'd like to implement your own hanldlers (or extend the existing ones with your own controllers), you can do so. All you need to do, is to defined the routes to the appropriate handlers in the button attributes. This also allows you to easily have multiple handlers for different use cases, if so desired. 164 | ``` html 165 |
174 |
175 | ``` 176 | ## File type validations 177 | The default implementation of accept checks the file's mime type or extension against this list. This is a comma separated list of mime types or file extensions. 178 | 179 | Eg.: image/*,application/pdf,.psd 180 | 181 | If the Dropzone is clickable this option will also be used as accept parameter on the hidden file input as well. 182 | 183 | ## License 184 | 185 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 186 | 187 | 188 | 189 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 190 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/Http/Controllers/LaradropController.php: -------------------------------------------------------------------------------- 1 | file = $file; 25 | } 26 | 27 | /** 28 | * Return html containers 29 | * 30 | * @return JsonResponse 31 | */ 32 | public function getContainers() 33 | { 34 | return response()->json([ 35 | 'status' => 'success', 36 | 'data' => [ 37 | 'main' => view('laradrop::mainContainer')->render(), 38 | 'preview' => view('laradrop::previewContainer')->render(), 39 | 'file' => view('laradrop::fileContainer')->render(), 40 | ], 41 | ]); 42 | } 43 | 44 | /** 45 | * Return all files which belong to the parent (pid), or root if no pid provided. 46 | * 47 | * @return JsonResponse 48 | */ 49 | public function index(Request $request) 50 | { 51 | try { 52 | $files = $this->file->get($request->get('pid')); 53 | 54 | return response()->json([ 55 | 'status' => 'success', 56 | 'data' => $files, 57 | ]); 58 | } catch (Exception $e) { 59 | return $this->handleError($e); 60 | } 61 | } 62 | 63 | /** 64 | * Error handler for this controller 65 | * 66 | * @param Exception $e 67 | * 68 | * @return JsonResponse 69 | */ 70 | private function handleError(Exception $e) 71 | { 72 | return response()->json([ 73 | 'msg' => $e->getMessage(), 74 | ], 401); 75 | } 76 | 77 | /** 78 | * Create a folder 79 | * 80 | * @return JsonResponse 81 | */ 82 | public function create(Request $request) 83 | { 84 | try { 85 | $fileData['alias'] = $request->get('filename') ? $request->get('filename') : date('m.d.Y - G:i:s'); 86 | $fileData['type'] = 'folder'; 87 | if ($request->get('pid') > 0) { 88 | $fileData['parent_id'] = $request->get('pid'); 89 | } 90 | 91 | $file = $this->file->create($fileData); 92 | 93 | return response()->json([ 94 | 'status' => 'success', 95 | 'id' => $file->id 96 | ]); 97 | } catch (Exception $e) { 98 | return $this->handleError($e); 99 | } 100 | } 101 | 102 | /** 103 | * Upload and store new file. 104 | * 105 | * @return JsonResponse 106 | */ 107 | public function store(Request $request) 108 | { 109 | try { 110 | 111 | if (!$request->hasFile('file')) { 112 | throw new Exception(trans('err.fileNotProvided')); 113 | } 114 | 115 | if (!$request->file('file')->isValid()) { 116 | throw new Exception(trans('err.invalidFile')); 117 | } 118 | 119 | /* 120 | * move file to temp location 121 | */ 122 | $fileExt = $request->file('file')->getClientOriginalExtension(); 123 | $fileName = str_replace('.' . $fileExt, '', $request->file('file')->getClientOriginalName()) . '-' . date('Ymdhis'); 124 | $mimeType = $request->file('file')->getMimeType(); 125 | $tmpStorage = storage_path(); 126 | $movedFileName = $fileName . '.' . $fileExt; 127 | $fileSize = $request->file('file')->getSize(); 128 | 129 | if ($fileSize > ((int)config('laradrop.max_upload_size') * 1000000)) { 130 | throw new Exception(trans('err.invalidFileSize')); 131 | } 132 | 133 | $request->file('file')->move($tmpStorage, $movedFileName); 134 | 135 | $disk = Storage::disk(config('laradrop.disk')); 136 | 137 | /* 138 | * create thumbnail if needed 139 | */ 140 | $fileData['has_thumbnail'] = 0; 141 | if ($fileSize <= ((int)config('laradrop.max_thumbnail_size') * 1000000) && in_array($mimeType, ["image/jpg", "image/jpeg", "image/png", "image/gif"])) { 142 | 143 | $thumbDims = config('laradrop.thumb_dimensions'); 144 | $img = Image::make($tmpStorage . '/' . $movedFileName); 145 | $img->resize($thumbDims['width'], $thumbDims['height']); 146 | $img->save($tmpStorage . '/_thumb_' . $movedFileName); 147 | 148 | // move thumbnail to final location 149 | $disk->put('_thumb_' . $movedFileName, fopen($tmpStorage . '/_thumb_' . $movedFileName, 'r+')); 150 | File::delete($tmpStorage . '/_thumb_' . $movedFileName); 151 | $fileData['has_thumbnail'] = 1; 152 | 153 | } 154 | 155 | /* 156 | * move uploaded file to final location 157 | */ 158 | $disk->put($movedFileName, fopen($tmpStorage . '/' . $movedFileName, 'r+')); 159 | File::delete($tmpStorage . '/' . $movedFileName); 160 | 161 | /* 162 | * save in db 163 | */ 164 | $fileData['filename'] = $movedFileName; 165 | $fileData['alias'] = $request->file('file')->getClientOriginalName(); 166 | $fileData['public_resource_url'] = config('laradrop.disk_public_url') . '/' . $movedFileName; 167 | $fileData['type'] = $fileExt; 168 | if ($request->get('pid') > 0) { 169 | $fileData['parent_id'] = $request->get('pid'); 170 | } 171 | $meta = $disk->getDriver()->getAdapter()->getMetaData($movedFileName); 172 | $meta['disk'] = config('laradrop.disk'); 173 | $fileData['meta'] = json_encode($meta); 174 | $file = $this->file->create($fileData); 175 | 176 | /* 177 | * fire 'file uploaded' event 178 | */ 179 | event(new FileWasUploaded([ 180 | 'file' => $file, 181 | 'postData' => $request->all(), 182 | ])); 183 | 184 | return $file; 185 | 186 | } catch (Exception $e) { 187 | 188 | // delete the file(s) 189 | if (isset($disk) && $disk) { 190 | 191 | if ($disk->has($movedFileName)) { 192 | $disk->delete($movedFileName); 193 | } 194 | 195 | if ($disk->has('_thumb_' . $movedFileName)) { 196 | $disk->delete('_thumb_' . $movedFileName); 197 | } 198 | } 199 | 200 | return $this->handleError($e); 201 | } 202 | } 203 | 204 | /** 205 | * Delete the resource 206 | * 207 | * @param $id 208 | * 209 | * @return JsonResponse 210 | */ 211 | public function destroy($id) 212 | { 213 | try { 214 | $this->file->destroy($id); 215 | 216 | return response()->json([ 217 | 'status' => 'success', 218 | ]); 219 | } catch (Exception $e) { 220 | return $this->handleError($e); 221 | } 222 | } 223 | 224 | /** 225 | * Move file 226 | * 227 | * @return JsonResponse 228 | */ 229 | public function move(Request $request) 230 | { 231 | 232 | try { 233 | $this->file->move($request->get('draggedId'), $request->get('droppedId')); 234 | 235 | return response()->json([ 236 | 'status' => 'success', 237 | ]); 238 | } catch (Exception $e) { 239 | return $this->handleError($e); 240 | } 241 | } 242 | 243 | /** 244 | * Update filename 245 | * 246 | * @param int $id 247 | * 248 | * @return JsonResponse 249 | */ 250 | public function update(Request $request, $id) 251 | { 252 | 253 | try { 254 | $file = $this->file->find($id); 255 | $file->filename = $request->get('filename'); 256 | $file->save(); 257 | 258 | return response()->json([ 259 | 'status' => 'success', 260 | ]); 261 | } catch (Exception $e) { 262 | return $this->handleError($e); 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/assets/js/laradrop.js: -------------------------------------------------------------------------------- 1 | jQuery.fn.laradrop = function(options) { 2 | Dropzone.autoDiscover = false; 3 | options = options == undefined ? {} : options; 4 | 5 | var laradropObj = jQuery(this), 6 | fileHandler = options.fileHandler ? options.fileHandler : ( laradropObj.attr('laradrop-upload-handler') ? laradropObj.attr('laradrop-upload-handler') : '/laradrop'), 7 | fileDeleteHandler = options.fileDeleteHandler ? options.fileDeleteHandler : ( laradropObj.attr('laradrop-file-delete-handler') ? laradropObj.attr('laradrop-file-delete-handler') : '/laradrop/0'), 8 | fileSrc = options.fileSrc ? options.fileSrc : ( laradropObj.attr('laradrop-file-source') ? laradropObj.attr('laradrop-file-source') : '/laradrop'), 9 | fileCreateHandler = options.fileCreateHandler ? options.fileCreateHandler : ( laradropObj.attr('laradrop-file-create-handler') ? laradropObj.attr('laradrop-file-create-handler') : '/laradrop/create'), 10 | fileMoveHandler = options.fileMoveHandler ? options.fileMoveHandler : ( laradropObj.attr('laradrop-file-move-handler') ? laradropObj.attr('laradrop-file-move-handler') : '/laradrop/move'), 11 | containersUrl = options.containersUrl ? options.containersUrl : ( laradropObj.attr('laradrop-containers') ? laradropObj.attr('laradrop-containers') : '/laradrop/containers'), 12 | csrfToken = options.csrfToken ? options.csrfToken : ( laradropObj.attr('laradrop-csrf-token') ? laradropObj.attr('laradrop-csrf-token') : null ), 13 | csrfTokenField = options.csrfTokenField ? options.csrfTokenField : ( laradropObj.attr('laradrop-csrf-token-field') ? laradropObj.attr('laradrop-csrf-token-field') : '_token'), 14 | actionConfirmationText = options.actionConfirmationText ? options.actionConfirmationText : 'Are you sure?', 15 | breadCrumbRootText = options.breadCrumbRootText ? options.breadCrumbRootText : 'Root Directory', 16 | folderImage = options.folderImage ? options.folderImage : '/vendor/jasekz/laradrop/img/genericThumbs/folder.png', 17 | onInsertCallback = options.onInsertCallback ? options.onInsertCallback : null, 18 | onSuccessCallback = options.onSuccessCallback ? options.onSuccessCallback : null, 19 | onErrorCallback = options.onErrorCallback ? options.onErrorCallback : null; 20 | uid = new Date().getTime(), 21 | laradropContainer=null, 22 | laradropPreviewContainer=null, 23 | dz=null, 24 | currentFolderId = null, 25 | breadCrumbs = [], 26 | customData = options.customData?options.customData:{}, 27 | views = { 28 | main: null, 29 | preview: null, 30 | file: null 31 | }; 32 | 33 | // init containers, default options & data 34 | jQuery.ajax({ 35 | url: containersUrl, 36 | type: 'GET', 37 | dataType: 'json', 38 | success: function(res) { 39 | 40 | // populate html templates 41 | views.main = res.data.main.replace('[[uid]]', uid); 42 | views.preview = res.data.preview; 43 | views.file = res.data.file; 44 | 45 | // setup file container 46 | laradropObj.append(getLaradropContainer()); 47 | laradropContainer = jQuery('#laradrop-container-'+uid), 48 | laradropPreviewsContainer = laradropContainer.find('.laradrop-previews'); 49 | laradropPreviewsContainer.attr('id', 'laradrop-previews-'+uid); 50 | 51 | // 'add folder' 52 | laradropContainer.find('.btn-add-folder').click(function(e){ 53 | e.preventDefault(); 54 | jQuery.ajax({ 55 | url: fileCreateHandler+'?pid='+currentFolderId, 56 | type: 'POST', 57 | dataType: 'json', 58 | headers: { 'X-CSRF-TOKEN': csrfToken }, 59 | success: function(res) { 60 | jQuery.get(fileSrc+'?pid='+currentFolderId, function(res){ 61 | displayMedia(res); 62 | }); 63 | }, 64 | error: function(jqXHR,textStatus,errorThrown){ 65 | handleError(jqXHR,textStatus,errorThrown); 66 | } 67 | }); 68 | }) 69 | 70 | // 'add files' 71 | $allowedFormats = $('#' + laradropContainer.attr('id')).parent().attr('laradrop-allow'); 72 | $acceptedFiles = $allowedFormats ? $allowedFormats : '*'; 73 | if(fileHandler) { 74 | dz = new Dropzone('#'+laradropContainer.attr('id')+' .btn-add-files', { 75 | acceptedFiles: $acceptedFiles, 76 | url: fileHandler, 77 | autoQueue:false, 78 | previewsContainer: "#laradrop-previews-"+uid, 79 | previewTemplate:getPreviewContainer(), 80 | parallelUploads:1, 81 | init: function(){ 82 | this.on("error", function(jqXHR,textStatus,errorThrown){ 83 | handleError({responseText:JSON.stringify(textStatus)}); 84 | }); 85 | this.on("success", function(status,res){ 86 | if(onSuccessCallback) { 87 | eval(onSuccessCallback(res)); 88 | } 89 | }); 90 | this.on("sending", function(file, xhr, data) { 91 | random = Math.random().toString(36).replace('.', ''); 92 | data.append(csrfTokenField, csrfToken); 93 | data.append('thumbId', random); 94 | data.append('filename', $(file.previewElement).find('#filename').val()); 95 | data.append('pid', currentFolderId); 96 | data.append('customData', JSON.stringify(customData)); 97 | }); 98 | this.on("complete", function(file){ 99 | dz.removeFile(file); 100 | jQuery.get(fileSrc+'?pid='+currentFolderId, function(res){ 101 | displayMedia(res); 102 | }); 103 | }); 104 | this.on("removedfile", function(file) { 105 | if(dz.files.length == 0){ 106 | laradropPreviewsContainer.hide(); 107 | $('.start').hide(); 108 | } 109 | }); 110 | this.on("addedfile", function(obj){ 111 | laradropPreviewsContainer.show(); 112 | $('.start').show(); 113 | $(".start").click(function() { 114 | dz.enqueueFiles(dz.getFilesWithStatus(Dropzone.ADDED)); 115 | }); 116 | }); 117 | } 118 | }); 119 | } 120 | 121 | // get data 122 | jQuery.ajax({ 123 | url: fileSrc, 124 | type: 'GET', 125 | dataType: 'json', 126 | success: function(res) { 127 | breadCrumbs.push({ 128 | id: 0, 129 | alias: breadCrumbRootText 130 | }); 131 | displayMedia(res); 132 | }, 133 | error: function(jqXHR,textStatus,errorThrown){ 134 | handleError(jqXHR,textStatus,errorThrown); 135 | } 136 | }); 137 | }, 138 | error: function(jqXHR,textStatus,errorThrown){ 139 | handleError(jqXHR,textStatus,errorThrown); 140 | } 141 | }); 142 | 143 | function displayMedia(res){ 144 | var out='', 145 | record='', 146 | re; 147 | 148 | jQuery.each(res.data, function(k,v){ 149 | record = getThumbnailContainer(Math.random().toString(36).replace('.', '')); 150 | 151 | jQuery.each(v, function(k2, v2){ 152 | re = new RegExp("\\[\\["+k2+"\\]\\]","g"); 153 | record = record.replace(re, v2); 154 | }); 155 | 156 | re = new RegExp("\\[\\[fileSrc\\]\\]","g"); 157 | if(v.type=='folder') { 158 | out+=record.replace(re, folderImage) 159 | .replace('laradrop-thumbnail','laradrop-thumbnail laradrop-thumbnail-image laradrop-folder'); 160 | } else { 161 | out+=record.replace(re, v.filename).replace(/laradrop\-droppable/g,''); 162 | } 163 | }); 164 | laradropContainer.find('.laradrop-body').html(out); 165 | 166 | laradropContainer.find('.laradrop-file-insert').click(function(){ 167 | var item = jQuery(this).closest('.laradrop-thumbnail'), 168 | thumbSrc = item.find('img').attr('src'), 169 | id = item.attr('file-id'); 170 | 171 | if(onInsertCallback) { 172 | eval(onInsertCallback({id:id, src:thumbSrc})); 173 | } 174 | }); 175 | 176 | laradropContainer.find('.laradrop-file-delete').click(function(e) { 177 | e.preventDefault(); 178 | var fileId = jQuery(this).closest('.laradrop-thumbnail').attr('file-id'); 179 | 180 | fileDeleteHandler = fileDeleteHandler.replace(fileDeleteHandler.substr(fileDeleteHandler.lastIndexOf('/')), '/'+fileId); 181 | 182 | if(!confirm(actionConfirmationText)) { 183 | return false; 184 | } 185 | 186 | jQuery.ajax({ 187 | url: fileDeleteHandler, 188 | type: 'DELETE', 189 | dataType: 'json', 190 | headers: { 'X-CSRF-TOKEN': csrfToken }, 191 | success: function(res) { 192 | jQuery.get(fileSrc+'?pid='+currentFolderId, function(files){ 193 | displayMedia(files); 194 | }); 195 | }, 196 | error: function(jqXHR,textStatus,errorThrown){ 197 | handleError(jqXHR,textStatus,errorThrown); 198 | } 199 | }); 200 | }); 201 | 202 | laradropContainer.find('.laradrop-folder img').click(function(){ 203 | var folder = jQuery(this).closest('.laradrop-folder'); 204 | currentFolderId=folder.attr('file-id'); 205 | breadCrumbs.push({ 206 | id: currentFolderId, 207 | alias: folder.find('.laradrop-filealias').text() 208 | }); 209 | 210 | jQuery.get(fileSrc+'?pid='+currentFolderId, function(res){ 211 | displayMedia(res); 212 | }); 213 | }); 214 | 215 | displayBreadCrumbs(); 216 | 217 | laradropContainer.find('.laradrop-draggable').draggable({ 218 | cancel: ".non-draggable", 219 | revert: "invalid", 220 | containment: "document", 221 | helper: "clone", 222 | cursor: "move", 223 | 224 | cursorAt: { top: 30, left: 15 }, 225 | helper: function( event ) { 226 | return $( "
  ↷  
" ); 227 | } 228 | }); 229 | 230 | laradropContainer.find('.laradrop-droppable').droppable({ 231 | accept: ".laradrop-draggable", 232 | hoverClass: "laradrop-droppable-hover", 233 | activeClass: "laradrop-droppable-highlight", 234 | drop: function( event, ui ) { 235 | var draggedId = ui.draggable.attr('file-id'), 236 | droppedId = jQuery(this).attr('file-id'); 237 | 238 | jQuery.ajax({ 239 | url: fileMoveHandler, 240 | type: 'POST', 241 | dataType: 'json', 242 | headers: { 'X-CSRF-TOKEN': csrfToken }, 243 | data: {'draggedId':draggedId, 'droppedId':droppedId, 'customData': JSON.stringify(customData)}, 244 | success: function(res) { 245 | jQuery.get(fileSrc+'?pid='+currentFolderId, function(res){ 246 | displayMedia(res); 247 | }); 248 | }, 249 | error: function(jqXHR,textStatus,errorThrown){ 250 | handleError(jqXHR,textStatus,errorThrown); 251 | } 252 | }); 253 | } 254 | }); 255 | } 256 | 257 | function displayBreadCrumbs(){ 258 | laradropContainer.find('.laradrop-breadcrumbs').remove(); 259 | var crumbs='
', 260 | length = breadCrumbs.length; 261 | 262 | jQuery.each(breadCrumbs, function(k, v){ 263 | if(k+1==length) { 264 | crumbs+=''+v.alias+''; 265 | }else{ 266 | crumbs+=''+v.alias+'»  '; 267 | } 268 | }); 269 | crumbs+='
'; 270 | laradropContainer.find('.laradrop-breadcrumbs-container').prepend(crumbs); 271 | 272 | laradropContainer.find('.laradrop-breadcrumb').click(function(e){ 273 | e.preventDefault(); 274 | currentFolderId=jQuery(this).attr('file-id'); 275 | var newCrumbs = []; 276 | jQuery.each(breadCrumbs, function(k, v){ 277 | newCrumbs.push(v); 278 | if(v.id==currentFolderId){ 279 | return false; 280 | } 281 | }); 282 | breadCrumbs=newCrumbs; 283 | 284 | jQuery.get(fileSrc+'?pid='+currentFolderId, function(res){ 285 | displayMedia(res); 286 | }); 287 | }); 288 | } 289 | 290 | function handleError(jqXHR,textStatus,errorThrown){ 291 | 292 | if(onErrorCallback) { 293 | eval(onErrorCallback(jqXHR,textStatus,errorThrown)); 294 | } else { 295 | alert(errorThrown); 296 | } 297 | } 298 | 299 | function getLaradropContainer() { 300 | return views.main; 301 | } 302 | 303 | function getThumbnailContainer(id) { 304 | return views.file; 305 | } 306 | 307 | function getPreviewContainer(){ 308 | return views.preview; 309 | } 310 | 311 | return laradropObj; 312 | } 313 | -------------------------------------------------------------------------------- /src/Jasekz/Laradrop/resources/assets/js/enyo.dropzone.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * 4 | * More info at [www.dropzonejs.com](http://www.dropzonejs.com) 5 | * 6 | * Copyright (c) 2012, Matias Meno 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | * 26 | */ 27 | 28 | (function() { 29 | var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, 30 | __slice = [].slice, 31 | __hasProp = {}.hasOwnProperty, 32 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 33 | 34 | noop = function() {}; 35 | 36 | Emitter = (function() { 37 | function Emitter() {} 38 | 39 | Emitter.prototype.addEventListener = Emitter.prototype.on; 40 | 41 | Emitter.prototype.on = function(event, fn) { 42 | this._callbacks = this._callbacks || {}; 43 | if (!this._callbacks[event]) { 44 | this._callbacks[event] = []; 45 | } 46 | this._callbacks[event].push(fn); 47 | return this; 48 | }; 49 | 50 | Emitter.prototype.emit = function() { 51 | var args, callback, callbacks, event, _i, _len; 52 | event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 53 | this._callbacks = this._callbacks || {}; 54 | callbacks = this._callbacks[event]; 55 | if (callbacks) { 56 | for (_i = 0, _len = callbacks.length; _i < _len; _i++) { 57 | callback = callbacks[_i]; 58 | callback.apply(this, args); 59 | } 60 | } 61 | return this; 62 | }; 63 | 64 | Emitter.prototype.removeListener = Emitter.prototype.off; 65 | 66 | Emitter.prototype.removeAllListeners = Emitter.prototype.off; 67 | 68 | Emitter.prototype.removeEventListener = Emitter.prototype.off; 69 | 70 | Emitter.prototype.off = function(event, fn) { 71 | var callback, callbacks, i, _i, _len; 72 | if (!this._callbacks || arguments.length === 0) { 73 | this._callbacks = {}; 74 | return this; 75 | } 76 | callbacks = this._callbacks[event]; 77 | if (!callbacks) { 78 | return this; 79 | } 80 | if (arguments.length === 1) { 81 | delete this._callbacks[event]; 82 | return this; 83 | } 84 | for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) { 85 | callback = callbacks[i]; 86 | if (callback === fn) { 87 | callbacks.splice(i, 1); 88 | break; 89 | } 90 | } 91 | return this; 92 | }; 93 | 94 | return Emitter; 95 | 96 | })(); 97 | 98 | Dropzone = (function(_super) { 99 | var extend, resolveOption; 100 | 101 | __extends(Dropzone, _super); 102 | 103 | Dropzone.prototype.Emitter = Emitter; 104 | 105 | 106 | /* 107 | This is a list of all available events you can register on a dropzone object. 108 | 109 | You can register an event handler like this: 110 | 111 | dropzone.on("dragEnter", function() { }); 112 | */ 113 | 114 | Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; 115 | 116 | Dropzone.prototype.defaultOptions = { 117 | url: null, 118 | method: "post", 119 | withCredentials: false, 120 | parallelUploads: 2, 121 | uploadMultiple: false, 122 | maxFilesize: 256, 123 | paramName: "file", 124 | createImageThumbnails: true, 125 | maxThumbnailFilesize: 10, 126 | thumbnailWidth: 120, 127 | thumbnailHeight: 120, 128 | filesizeBase: 1000, 129 | maxFiles: null, 130 | filesizeBase: 1000, 131 | params: {}, 132 | clickable: true, 133 | ignoreHiddenFiles: true, 134 | acceptedFiles: null, 135 | acceptedMimeTypes: null, 136 | autoProcessQueue: true, 137 | autoQueue: true, 138 | addRemoveLinks: false, 139 | previewsContainer: null, 140 | capture: null, 141 | dictDefaultMessage: "Drop files here to upload", 142 | dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", 143 | dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", 144 | dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", 145 | dictInvalidFileType: "You can't upload files of this type.", 146 | dictResponseError: "Server responded with {{statusCode}} code.", 147 | dictCancelUpload: "Cancel upload", 148 | dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", 149 | dictRemoveFile: "Remove file", 150 | dictRemoveFileConfirmation: null, 151 | dictMaxFilesExceeded: "You can not upload any more files.", 152 | accept: function(file, done) { 153 | return done(); 154 | }, 155 | init: function() { 156 | return noop; 157 | }, 158 | forceFallback: false, 159 | fallback: function() { 160 | var child, messageElement, span, _i, _len, _ref; 161 | this.element.className = "" + this.element.className + " dz-browser-not-supported"; 162 | _ref = this.element.getElementsByTagName("div"); 163 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 164 | child = _ref[_i]; 165 | if (/(^| )dz-message($| )/.test(child.className)) { 166 | messageElement = child; 167 | child.className = "dz-message"; 168 | continue; 169 | } 170 | } 171 | if (!messageElement) { 172 | messageElement = Dropzone.createElement("
"); 173 | this.element.appendChild(messageElement); 174 | } 175 | span = messageElement.getElementsByTagName("span")[0]; 176 | if (span) { 177 | span.textContent = this.options.dictFallbackMessage; 178 | } 179 | return this.element.appendChild(this.getFallbackForm()); 180 | }, 181 | resize: function(file) { 182 | var info, srcRatio, trgRatio; 183 | info = { 184 | srcX: 0, 185 | srcY: 0, 186 | srcWidth: file.width, 187 | srcHeight: file.height 188 | }; 189 | srcRatio = file.width / file.height; 190 | info.optWidth = this.options.thumbnailWidth; 191 | info.optHeight = this.options.thumbnailHeight; 192 | if ((info.optWidth == null) && (info.optHeight == null)) { 193 | info.optWidth = info.srcWidth; 194 | info.optHeight = info.srcHeight; 195 | } else if (info.optWidth == null) { 196 | info.optWidth = srcRatio * info.optHeight; 197 | } else if (info.optHeight == null) { 198 | info.optHeight = (1 / srcRatio) * info.optWidth; 199 | } 200 | trgRatio = info.optWidth / info.optHeight; 201 | if (file.height < info.optHeight || file.width < info.optWidth) { 202 | info.trgHeight = info.srcHeight; 203 | info.trgWidth = info.srcWidth; 204 | } else { 205 | if (srcRatio > trgRatio) { 206 | info.srcHeight = file.height; 207 | info.srcWidth = info.srcHeight * trgRatio; 208 | } else { 209 | info.srcWidth = file.width; 210 | info.srcHeight = info.srcWidth / trgRatio; 211 | } 212 | } 213 | info.srcX = (file.width - info.srcWidth) / 2; 214 | info.srcY = (file.height - info.srcHeight) / 2; 215 | return info; 216 | }, 217 | 218 | /* 219 | Those functions register themselves to the events on init and handle all 220 | the user interface specific stuff. Overwriting them won't break the upload 221 | but can break the way it's displayed. 222 | You can overwrite them if you don't like the default behavior. If you just 223 | want to add an additional event handler, register it on the dropzone object 224 | and don't overwrite those options. 225 | */ 226 | drop: function(e) { 227 | return this.element.classList.remove("dz-drag-hover"); 228 | }, 229 | dragstart: noop, 230 | dragend: function(e) { 231 | return this.element.classList.remove("dz-drag-hover"); 232 | }, 233 | dragenter: function(e) { 234 | return this.element.classList.add("dz-drag-hover"); 235 | }, 236 | dragover: function(e) { 237 | return this.element.classList.add("dz-drag-hover"); 238 | }, 239 | dragleave: function(e) { 240 | return this.element.classList.remove("dz-drag-hover"); 241 | }, 242 | paste: noop, 243 | reset: function() { 244 | return this.element.classList.remove("dz-started"); 245 | }, 246 | addedfile: function(file) { 247 | var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; 248 | if (this.element === this.previewsContainer) { 249 | this.element.classList.add("dz-started"); 250 | } 251 | if (this.previewsContainer) { 252 | file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); 253 | file.previewTemplate = file.previewElement; 254 | this.previewsContainer.appendChild(file.previewElement); 255 | _ref = file.previewElement.querySelectorAll("[data-dz-name]"); 256 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 257 | node = _ref[_i]; 258 | node.textContent = file.name; 259 | } 260 | _ref1 = file.previewElement.querySelectorAll("[data-dz-size]"); 261 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 262 | node = _ref1[_j]; 263 | node.innerHTML = this.filesize(file.size); 264 | } 265 | if (this.options.addRemoveLinks) { 266 | file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); 267 | file.previewElement.appendChild(file._removeLink); 268 | } 269 | removeFileEvent = (function(_this) { 270 | return function(e) { 271 | e.preventDefault(); 272 | e.stopPropagation(); 273 | if (file.status === Dropzone.UPLOADING) { 274 | return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() { 275 | return _this.removeFile(file); 276 | }); 277 | } else { 278 | if (_this.options.dictRemoveFileConfirmation) { 279 | return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() { 280 | return _this.removeFile(file); 281 | }); 282 | } else { 283 | return _this.removeFile(file); 284 | } 285 | } 286 | }; 287 | })(this); 288 | _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]"); 289 | _results = []; 290 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { 291 | removeLink = _ref2[_k]; 292 | _results.push(removeLink.addEventListener("click", removeFileEvent)); 293 | } 294 | return _results; 295 | } 296 | }, 297 | removedfile: function(file) { 298 | var _ref; 299 | if (file.previewElement) { 300 | if ((_ref = file.previewElement) != null) { 301 | _ref.parentNode.removeChild(file.previewElement); 302 | } 303 | } 304 | return this._updateMaxFilesReachedClass(); 305 | }, 306 | thumbnail: function(file, dataUrl) { 307 | var thumbnailElement, _i, _len, _ref; 308 | if (file.previewElement) { 309 | file.previewElement.classList.remove("dz-file-preview"); 310 | _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]"); 311 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 312 | thumbnailElement = _ref[_i]; 313 | thumbnailElement.alt = file.name; 314 | thumbnailElement.src = dataUrl; 315 | } 316 | return setTimeout(((function(_this) { 317 | return function() { 318 | return file.previewElement.classList.add("dz-image-preview"); 319 | }; 320 | })(this)), 1); 321 | } 322 | }, 323 | error: function(file, message) { 324 | var node, _i, _len, _ref, _results; 325 | if (file.previewElement) { 326 | file.previewElement.classList.add("dz-error"); 327 | if (typeof message !== "String" && message.error) { 328 | message = message.error; 329 | } 330 | _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]"); 331 | _results = []; 332 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 333 | node = _ref[_i]; 334 | _results.push(node.textContent = message); 335 | } 336 | return _results; 337 | } 338 | }, 339 | errormultiple: noop, 340 | processing: function(file) { 341 | if (file.previewElement) { 342 | file.previewElement.classList.add("dz-processing"); 343 | if (file._removeLink) { 344 | return file._removeLink.textContent = this.options.dictCancelUpload; 345 | } 346 | } 347 | }, 348 | processingmultiple: noop, 349 | uploadprogress: function(file, progress, bytesSent) { 350 | var node, _i, _len, _ref, _results; 351 | if (file.previewElement) { 352 | _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"); 353 | _results = []; 354 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 355 | node = _ref[_i]; 356 | if (node.nodeName === 'PROGRESS') { 357 | _results.push(node.value = progress); 358 | } else { 359 | _results.push(node.style.width = "" + progress + "%"); 360 | } 361 | } 362 | return _results; 363 | } 364 | }, 365 | totaluploadprogress: noop, 366 | sending: noop, 367 | sendingmultiple: noop, 368 | success: function(file) { 369 | if (file.previewElement) { 370 | return file.previewElement.classList.add("dz-success"); 371 | } 372 | }, 373 | successmultiple: noop, 374 | canceled: function(file) { 375 | return this.emit("error", file, "Upload canceled."); 376 | }, 377 | canceledmultiple: noop, 378 | complete: function(file) { 379 | if (file._removeLink) { 380 | file._removeLink.textContent = this.options.dictRemoveFile; 381 | } 382 | if (file.previewElement) { 383 | return file.previewElement.classList.add("dz-complete"); 384 | } 385 | }, 386 | completemultiple: noop, 387 | maxfilesexceeded: noop, 388 | maxfilesreached: noop, 389 | queuecomplete: noop, 390 | 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
" 391 | }; 392 | 393 | extend = function() { 394 | var key, object, objects, target, val, _i, _len; 395 | target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 396 | for (_i = 0, _len = objects.length; _i < _len; _i++) { 397 | object = objects[_i]; 398 | for (key in object) { 399 | val = object[key]; 400 | target[key] = val; 401 | } 402 | } 403 | return target; 404 | }; 405 | 406 | function Dropzone(element, options) { 407 | var elementOptions, fallback, _ref; 408 | this.element = element; 409 | this.version = Dropzone.version; 410 | this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, ""); 411 | this.clickableElements = []; 412 | this.listeners = []; 413 | this.files = []; 414 | if (typeof this.element === "string") { 415 | this.element = document.querySelector(this.element); 416 | } 417 | if (!(this.element && (this.element.nodeType != null))) { 418 | throw new Error("Invalid dropzone element."); 419 | } 420 | if (this.element.dropzone) { 421 | throw new Error("Dropzone already attached."); 422 | } 423 | Dropzone.instances.push(this); 424 | this.element.dropzone = this; 425 | elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {}; 426 | this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {}); 427 | if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { 428 | return this.options.fallback.call(this); 429 | } 430 | if (this.options.url == null) { 431 | this.options.url = this.element.getAttribute("action"); 432 | } 433 | if (!this.options.url) { 434 | throw new Error("No URL provided."); 435 | } 436 | if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { 437 | throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); 438 | } 439 | if (this.options.acceptedMimeTypes) { 440 | this.options.acceptedFiles = this.options.acceptedMimeTypes; 441 | delete this.options.acceptedMimeTypes; 442 | } 443 | this.options.method = this.options.method.toUpperCase(); 444 | if ((fallback = this.getExistingFallback()) && fallback.parentNode) { 445 | fallback.parentNode.removeChild(fallback); 446 | } 447 | if (this.options.previewsContainer !== false) { 448 | if (this.options.previewsContainer) { 449 | this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer"); 450 | } else { 451 | this.previewsContainer = this.element; 452 | } 453 | } 454 | if (this.options.clickable) { 455 | if (this.options.clickable === true) { 456 | this.clickableElements = [this.element]; 457 | } else { 458 | this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable"); 459 | } 460 | } 461 | this.init(); 462 | } 463 | 464 | Dropzone.prototype.getAcceptedFiles = function() { 465 | var file, _i, _len, _ref, _results; 466 | _ref = this.files; 467 | _results = []; 468 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 469 | file = _ref[_i]; 470 | if (file.accepted) { 471 | _results.push(file); 472 | } 473 | } 474 | return _results; 475 | }; 476 | 477 | Dropzone.prototype.getRejectedFiles = function() { 478 | var file, _i, _len, _ref, _results; 479 | _ref = this.files; 480 | _results = []; 481 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 482 | file = _ref[_i]; 483 | if (!file.accepted) { 484 | _results.push(file); 485 | } 486 | } 487 | return _results; 488 | }; 489 | 490 | Dropzone.prototype.getFilesWithStatus = function(status) { 491 | var file, _i, _len, _ref, _results; 492 | _ref = this.files; 493 | _results = []; 494 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 495 | file = _ref[_i]; 496 | if (file.status === status) { 497 | _results.push(file); 498 | } 499 | } 500 | return _results; 501 | }; 502 | 503 | Dropzone.prototype.getQueuedFiles = function() { 504 | return this.getFilesWithStatus(Dropzone.QUEUED); 505 | }; 506 | 507 | Dropzone.prototype.getUploadingFiles = function() { 508 | return this.getFilesWithStatus(Dropzone.UPLOADING); 509 | }; 510 | 511 | Dropzone.prototype.getActiveFiles = function() { 512 | var file, _i, _len, _ref, _results; 513 | _ref = this.files; 514 | _results = []; 515 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 516 | file = _ref[_i]; 517 | if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) { 518 | _results.push(file); 519 | } 520 | } 521 | return _results; 522 | }; 523 | 524 | Dropzone.prototype.init = function() { 525 | var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1; 526 | if (this.element.tagName === "form") { 527 | this.element.setAttribute("enctype", "multipart/form-data"); 528 | } 529 | if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) { 530 | this.element.appendChild(Dropzone.createElement("
" + this.options.dictDefaultMessage + "
")); 531 | } 532 | if (this.clickableElements.length) { 533 | setupHiddenFileInput = (function(_this) { 534 | return function() { 535 | if (_this.hiddenFileInput) { 536 | document.body.removeChild(_this.hiddenFileInput); 537 | } 538 | _this.hiddenFileInput = document.createElement("input"); 539 | _this.hiddenFileInput.setAttribute("type", "file"); 540 | if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) { 541 | _this.hiddenFileInput.setAttribute("multiple", "multiple"); 542 | } 543 | _this.hiddenFileInput.className = "dz-hidden-input"; 544 | if (_this.options.acceptedFiles != null) { 545 | _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles); 546 | } 547 | if (_this.options.capture != null) { 548 | _this.hiddenFileInput.setAttribute("capture", _this.options.capture); 549 | } 550 | _this.hiddenFileInput.style.visibility = "hidden"; 551 | _this.hiddenFileInput.style.position = "absolute"; 552 | _this.hiddenFileInput.style.top = "0"; 553 | _this.hiddenFileInput.style.left = "0"; 554 | _this.hiddenFileInput.style.height = "0"; 555 | _this.hiddenFileInput.style.width = "0"; 556 | document.body.appendChild(_this.hiddenFileInput); 557 | return _this.hiddenFileInput.addEventListener("change", function() { 558 | var file, files, _i, _len; 559 | files = _this.hiddenFileInput.files; 560 | if (files.length) { 561 | for (_i = 0, _len = files.length; _i < _len; _i++) { 562 | file = files[_i]; 563 | _this.addFile(file); 564 | } 565 | } 566 | return setupHiddenFileInput(); 567 | }); 568 | }; 569 | })(this); 570 | setupHiddenFileInput(); 571 | } 572 | this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL; 573 | _ref1 = this.events; 574 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) { 575 | eventName = _ref1[_i]; 576 | this.on(eventName, this.options[eventName]); 577 | } 578 | this.on("uploadprogress", (function(_this) { 579 | return function() { 580 | return _this.updateTotalUploadProgress(); 581 | }; 582 | })(this)); 583 | this.on("removedfile", (function(_this) { 584 | return function() { 585 | return _this.updateTotalUploadProgress(); 586 | }; 587 | })(this)); 588 | this.on("canceled", (function(_this) { 589 | return function(file) { 590 | return _this.emit("complete", file); 591 | }; 592 | })(this)); 593 | this.on("complete", (function(_this) { 594 | return function(file) { 595 | if (_this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) { 596 | return setTimeout((function() { 597 | return _this.emit("queuecomplete"); 598 | }), 0); 599 | } 600 | }; 601 | })(this)); 602 | noPropagation = function(e) { 603 | e.stopPropagation(); 604 | if (e.preventDefault) { 605 | return e.preventDefault(); 606 | } else { 607 | return e.returnValue = false; 608 | } 609 | }; 610 | this.listeners = [ 611 | { 612 | element: this.element, 613 | events: { 614 | "dragstart": (function(_this) { 615 | return function(e) { 616 | return _this.emit("dragstart", e); 617 | }; 618 | })(this), 619 | "dragenter": (function(_this) { 620 | return function(e) { 621 | noPropagation(e); 622 | return _this.emit("dragenter", e); 623 | }; 624 | })(this), 625 | "dragover": (function(_this) { 626 | return function(e) { 627 | var efct; 628 | try { 629 | efct = e.dataTransfer.effectAllowed; 630 | } catch (_error) {} 631 | e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; 632 | noPropagation(e); 633 | return _this.emit("dragover", e); 634 | }; 635 | })(this), 636 | "dragleave": (function(_this) { 637 | return function(e) { 638 | return _this.emit("dragleave", e); 639 | }; 640 | })(this), 641 | "drop": (function(_this) { 642 | return function(e) { 643 | noPropagation(e); 644 | return _this.drop(e); 645 | }; 646 | })(this), 647 | "dragend": (function(_this) { 648 | return function(e) { 649 | return _this.emit("dragend", e); 650 | }; 651 | })(this) 652 | } 653 | } 654 | ]; 655 | this.clickableElements.forEach((function(_this) { 656 | return function(clickableElement) { 657 | return _this.listeners.push({ 658 | element: clickableElement, 659 | events: { 660 | "click": function(evt) { 661 | if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) { 662 | return _this.hiddenFileInput.click(); 663 | } 664 | } 665 | } 666 | }); 667 | }; 668 | })(this)); 669 | this.enable(); 670 | return this.options.init.call(this); 671 | }; 672 | 673 | Dropzone.prototype.destroy = function() { 674 | var _ref; 675 | this.disable(); 676 | this.removeAllFiles(true); 677 | if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) { 678 | this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); 679 | this.hiddenFileInput = null; 680 | } 681 | delete this.element.dropzone; 682 | return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); 683 | }; 684 | 685 | Dropzone.prototype.updateTotalUploadProgress = function() { 686 | var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref; 687 | totalBytesSent = 0; 688 | totalBytes = 0; 689 | activeFiles = this.getActiveFiles(); 690 | if (activeFiles.length) { 691 | _ref = this.getActiveFiles(); 692 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 693 | file = _ref[_i]; 694 | totalBytesSent += file.upload.bytesSent; 695 | totalBytes += file.upload.total; 696 | } 697 | totalUploadProgress = 100 * totalBytesSent / totalBytes; 698 | } else { 699 | totalUploadProgress = 100; 700 | } 701 | return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); 702 | }; 703 | 704 | Dropzone.prototype._getParamName = function(n) { 705 | if (typeof this.options.paramName === "function") { 706 | return this.options.paramName(n); 707 | } else { 708 | return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : ""); 709 | } 710 | }; 711 | 712 | Dropzone.prototype.getFallbackForm = function() { 713 | var existingFallback, fields, fieldsString, form; 714 | if (existingFallback = this.getExistingFallback()) { 715 | return existingFallback; 716 | } 717 | fieldsString = "
"; 718 | if (this.options.dictFallbackText) { 719 | fieldsString += "

" + this.options.dictFallbackText + "

"; 720 | } 721 | fieldsString += "
"; 722 | fields = Dropzone.createElement(fieldsString); 723 | if (this.element.tagName !== "FORM") { 724 | form = Dropzone.createElement("
"); 725 | form.appendChild(fields); 726 | } else { 727 | this.element.setAttribute("enctype", "multipart/form-data"); 728 | this.element.setAttribute("method", this.options.method); 729 | } 730 | return form != null ? form : fields; 731 | }; 732 | 733 | Dropzone.prototype.getExistingFallback = function() { 734 | var fallback, getFallback, tagName, _i, _len, _ref; 735 | getFallback = function(elements) { 736 | var el, _i, _len; 737 | for (_i = 0, _len = elements.length; _i < _len; _i++) { 738 | el = elements[_i]; 739 | if (/(^| )fallback($| )/.test(el.className)) { 740 | return el; 741 | } 742 | } 743 | }; 744 | _ref = ["div", "form"]; 745 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 746 | tagName = _ref[_i]; 747 | if (fallback = getFallback(this.element.getElementsByTagName(tagName))) { 748 | return fallback; 749 | } 750 | } 751 | }; 752 | 753 | Dropzone.prototype.setupEventListeners = function() { 754 | var elementListeners, event, listener, _i, _len, _ref, _results; 755 | _ref = this.listeners; 756 | _results = []; 757 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 758 | elementListeners = _ref[_i]; 759 | _results.push((function() { 760 | var _ref1, _results1; 761 | _ref1 = elementListeners.events; 762 | _results1 = []; 763 | for (event in _ref1) { 764 | listener = _ref1[event]; 765 | _results1.push(elementListeners.element.addEventListener(event, listener, false)); 766 | } 767 | return _results1; 768 | })()); 769 | } 770 | return _results; 771 | }; 772 | 773 | Dropzone.prototype.removeEventListeners = function() { 774 | var elementListeners, event, listener, _i, _len, _ref, _results; 775 | _ref = this.listeners; 776 | _results = []; 777 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 778 | elementListeners = _ref[_i]; 779 | _results.push((function() { 780 | var _ref1, _results1; 781 | _ref1 = elementListeners.events; 782 | _results1 = []; 783 | for (event in _ref1) { 784 | listener = _ref1[event]; 785 | _results1.push(elementListeners.element.removeEventListener(event, listener, false)); 786 | } 787 | return _results1; 788 | })()); 789 | } 790 | return _results; 791 | }; 792 | 793 | Dropzone.prototype.disable = function() { 794 | var file, _i, _len, _ref, _results; 795 | this.clickableElements.forEach(function(element) { 796 | return element.classList.remove("dz-clickable"); 797 | }); 798 | this.removeEventListeners(); 799 | _ref = this.files; 800 | _results = []; 801 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 802 | file = _ref[_i]; 803 | _results.push(this.cancelUpload(file)); 804 | } 805 | return _results; 806 | }; 807 | 808 | Dropzone.prototype.enable = function() { 809 | this.clickableElements.forEach(function(element) { 810 | return element.classList.add("dz-clickable"); 811 | }); 812 | return this.setupEventListeners(); 813 | }; 814 | 815 | Dropzone.prototype.filesize = function(size) { 816 | var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len; 817 | units = ['TB', 'GB', 'MB', 'KB', 'b']; 818 | selectedSize = selectedUnit = null; 819 | for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) { 820 | unit = units[i]; 821 | cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; 822 | if (size >= cutoff) { 823 | selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); 824 | selectedUnit = unit; 825 | break; 826 | } 827 | } 828 | selectedSize = Math.round(10 * selectedSize) / 10; 829 | return "" + selectedSize + " " + selectedUnit; 830 | }; 831 | 832 | Dropzone.prototype._updateMaxFilesReachedClass = function() { 833 | if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { 834 | if (this.getAcceptedFiles().length === this.options.maxFiles) { 835 | this.emit('maxfilesreached', this.files); 836 | } 837 | return this.element.classList.add("dz-max-files-reached"); 838 | } else { 839 | return this.element.classList.remove("dz-max-files-reached"); 840 | } 841 | }; 842 | 843 | Dropzone.prototype.drop = function(e) { 844 | var files, items; 845 | if (!e.dataTransfer) { 846 | return; 847 | } 848 | this.emit("drop", e); 849 | files = e.dataTransfer.files; 850 | if (files.length) { 851 | items = e.dataTransfer.items; 852 | if (items && items.length && (items[0].webkitGetAsEntry != null)) { 853 | this._addFilesFromItems(items); 854 | } else { 855 | this.handleFiles(files); 856 | } 857 | } 858 | }; 859 | 860 | Dropzone.prototype.paste = function(e) { 861 | var items, _ref; 862 | if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) { 863 | return; 864 | } 865 | this.emit("paste", e); 866 | items = e.clipboardData.items; 867 | if (items.length) { 868 | return this._addFilesFromItems(items); 869 | } 870 | }; 871 | 872 | Dropzone.prototype.handleFiles = function(files) { 873 | var file, _i, _len, _results; 874 | _results = []; 875 | for (_i = 0, _len = files.length; _i < _len; _i++) { 876 | file = files[_i]; 877 | _results.push(this.addFile(file)); 878 | } 879 | return _results; 880 | }; 881 | 882 | Dropzone.prototype._addFilesFromItems = function(items) { 883 | var entry, item, _i, _len, _results; 884 | _results = []; 885 | for (_i = 0, _len = items.length; _i < _len; _i++) { 886 | item = items[_i]; 887 | if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) { 888 | if (entry.isFile) { 889 | _results.push(this.addFile(item.getAsFile())); 890 | } else if (entry.isDirectory) { 891 | _results.push(this._addFilesFromDirectory(entry, entry.name)); 892 | } else { 893 | _results.push(void 0); 894 | } 895 | } else if (item.getAsFile != null) { 896 | if ((item.kind == null) || item.kind === "file") { 897 | _results.push(this.addFile(item.getAsFile())); 898 | } else { 899 | _results.push(void 0); 900 | } 901 | } else { 902 | _results.push(void 0); 903 | } 904 | } 905 | return _results; 906 | }; 907 | 908 | Dropzone.prototype._addFilesFromDirectory = function(directory, path) { 909 | var dirReader, entriesReader; 910 | dirReader = directory.createReader(); 911 | entriesReader = (function(_this) { 912 | return function(entries) { 913 | var entry, _i, _len; 914 | for (_i = 0, _len = entries.length; _i < _len; _i++) { 915 | entry = entries[_i]; 916 | if (entry.isFile) { 917 | entry.file(function(file) { 918 | if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { 919 | return; 920 | } 921 | file.fullPath = "" + path + "/" + file.name; 922 | return _this.addFile(file); 923 | }); 924 | } else if (entry.isDirectory) { 925 | _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name); 926 | } 927 | } 928 | }; 929 | })(this); 930 | return dirReader.readEntries(entriesReader, function(error) { 931 | return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0; 932 | }); 933 | }; 934 | 935 | Dropzone.prototype.accept = function(file, done) { 936 | if (file.size > this.options.maxFilesize * 1024 * 1024) { 937 | return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)); 938 | } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { 939 | return done(this.options.dictInvalidFileType); 940 | } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { 941 | done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)); 942 | return this.emit("maxfilesexceeded", file); 943 | } else { 944 | return this.options.accept.call(this, file, done); 945 | } 946 | }; 947 | 948 | Dropzone.prototype.addFile = function(file) { 949 | file.upload = { 950 | progress: 0, 951 | total: file.size, 952 | bytesSent: 0 953 | }; 954 | this.files.push(file); 955 | file.status = Dropzone.ADDED; 956 | this.emit("addedfile", file); 957 | this._enqueueThumbnail(file); 958 | return this.accept(file, (function(_this) { 959 | return function(error) { 960 | if (error) { 961 | file.accepted = false; 962 | _this._errorProcessing([file], error); 963 | } else { 964 | file.accepted = true; 965 | if (_this.options.autoQueue) { 966 | _this.enqueueFile(file); 967 | } 968 | } 969 | return _this._updateMaxFilesReachedClass(); 970 | }; 971 | })(this)); 972 | }; 973 | 974 | Dropzone.prototype.enqueueFiles = function(files) { 975 | var file, _i, _len; 976 | for (_i = 0, _len = files.length; _i < _len; _i++) { 977 | file = files[_i]; 978 | this.enqueueFile(file); 979 | } 980 | return null; 981 | }; 982 | 983 | Dropzone.prototype.enqueueFile = function(file) { 984 | if (file.status === Dropzone.ADDED && file.accepted === true) { 985 | file.status = Dropzone.QUEUED; 986 | if (this.options.autoProcessQueue) { 987 | return setTimeout(((function(_this) { 988 | return function() { 989 | return _this.processQueue(); 990 | }; 991 | })(this)), 0); 992 | } 993 | } else { 994 | throw new Error("This file can't be queued because it has already been processed or was rejected."); 995 | } 996 | }; 997 | 998 | Dropzone.prototype._thumbnailQueue = []; 999 | 1000 | Dropzone.prototype._processingThumbnail = false; 1001 | 1002 | Dropzone.prototype._enqueueThumbnail = function(file) { 1003 | if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) { 1004 | this._thumbnailQueue.push(file); 1005 | return setTimeout(((function(_this) { 1006 | return function() { 1007 | return _this._processThumbnailQueue(); 1008 | }; 1009 | })(this)), 0); 1010 | } 1011 | }; 1012 | 1013 | Dropzone.prototype._processThumbnailQueue = function() { 1014 | if (this._processingThumbnail || this._thumbnailQueue.length === 0) { 1015 | return; 1016 | } 1017 | this._processingThumbnail = true; 1018 | return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) { 1019 | return function() { 1020 | _this._processingThumbnail = false; 1021 | return _this._processThumbnailQueue(); 1022 | }; 1023 | })(this)); 1024 | }; 1025 | 1026 | Dropzone.prototype.removeFile = function(file) { 1027 | if (file.status === Dropzone.UPLOADING) { 1028 | this.cancelUpload(file); 1029 | } 1030 | this.files = without(this.files, file); 1031 | this.emit("removedfile", file); 1032 | if (this.files.length === 0) { 1033 | return this.emit("reset"); 1034 | } 1035 | }; 1036 | 1037 | Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) { 1038 | var file, _i, _len, _ref; 1039 | if (cancelIfNecessary == null) { 1040 | cancelIfNecessary = false; 1041 | } 1042 | _ref = this.files.slice(); 1043 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1044 | file = _ref[_i]; 1045 | if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { 1046 | this.removeFile(file); 1047 | } 1048 | } 1049 | return null; 1050 | }; 1051 | 1052 | Dropzone.prototype.createThumbnail = function(file, callback) { 1053 | var fileReader; 1054 | fileReader = new FileReader; 1055 | fileReader.onload = (function(_this) { 1056 | return function() { 1057 | if (file.type === "image/svg+xml") { 1058 | _this.emit("thumbnail", file, fileReader.result); 1059 | if (callback != null) { 1060 | callback(); 1061 | } 1062 | return; 1063 | } 1064 | return _this.createThumbnailFromUrl(file, fileReader.result, callback); 1065 | }; 1066 | })(this); 1067 | return fileReader.readAsDataURL(file); 1068 | }; 1069 | 1070 | Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback) { 1071 | var img; 1072 | img = document.createElement("img"); 1073 | img.onload = (function(_this) { 1074 | return function() { 1075 | var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3; 1076 | file.width = img.width; 1077 | file.height = img.height; 1078 | resizeInfo = _this.options.resize.call(_this, file); 1079 | if (resizeInfo.trgWidth == null) { 1080 | resizeInfo.trgWidth = resizeInfo.optWidth; 1081 | } 1082 | if (resizeInfo.trgHeight == null) { 1083 | resizeInfo.trgHeight = resizeInfo.optHeight; 1084 | } 1085 | canvas = document.createElement("canvas"); 1086 | ctx = canvas.getContext("2d"); 1087 | canvas.width = resizeInfo.trgWidth; 1088 | canvas.height = resizeInfo.trgHeight; 1089 | drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); 1090 | thumbnail = canvas.toDataURL("image/png"); 1091 | _this.emit("thumbnail", file, thumbnail); 1092 | if (callback != null) { 1093 | return callback(); 1094 | } 1095 | }; 1096 | })(this); 1097 | if (callback != null) { 1098 | img.onerror = callback; 1099 | } 1100 | return img.src = imageUrl; 1101 | }; 1102 | 1103 | Dropzone.prototype.processQueue = function() { 1104 | var i, parallelUploads, processingLength, queuedFiles; 1105 | parallelUploads = this.options.parallelUploads; 1106 | processingLength = this.getUploadingFiles().length; 1107 | i = processingLength; 1108 | if (processingLength >= parallelUploads) { 1109 | return; 1110 | } 1111 | queuedFiles = this.getQueuedFiles(); 1112 | if (!(queuedFiles.length > 0)) { 1113 | return; 1114 | } 1115 | if (this.options.uploadMultiple) { 1116 | return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength)); 1117 | } else { 1118 | while (i < parallelUploads) { 1119 | if (!queuedFiles.length) { 1120 | return; 1121 | } 1122 | this.processFile(queuedFiles.shift()); 1123 | i++; 1124 | } 1125 | } 1126 | }; 1127 | 1128 | Dropzone.prototype.processFile = function(file) { 1129 | return this.processFiles([file]); 1130 | }; 1131 | 1132 | Dropzone.prototype.processFiles = function(files) { 1133 | var file, _i, _len; 1134 | for (_i = 0, _len = files.length; _i < _len; _i++) { 1135 | file = files[_i]; 1136 | file.processing = true; 1137 | file.status = Dropzone.UPLOADING; 1138 | this.emit("processing", file); 1139 | } 1140 | if (this.options.uploadMultiple) { 1141 | this.emit("processingmultiple", files); 1142 | } 1143 | return this.uploadFiles(files); 1144 | }; 1145 | 1146 | Dropzone.prototype._getFilesWithXhr = function(xhr) { 1147 | var file, files; 1148 | return files = (function() { 1149 | var _i, _len, _ref, _results; 1150 | _ref = this.files; 1151 | _results = []; 1152 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1153 | file = _ref[_i]; 1154 | if (file.xhr === xhr) { 1155 | _results.push(file); 1156 | } 1157 | } 1158 | return _results; 1159 | }).call(this); 1160 | }; 1161 | 1162 | Dropzone.prototype.cancelUpload = function(file) { 1163 | var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref; 1164 | if (file.status === Dropzone.UPLOADING) { 1165 | groupedFiles = this._getFilesWithXhr(file.xhr); 1166 | for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) { 1167 | groupedFile = groupedFiles[_i]; 1168 | groupedFile.status = Dropzone.CANCELED; 1169 | } 1170 | file.xhr.abort(); 1171 | for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) { 1172 | groupedFile = groupedFiles[_j]; 1173 | this.emit("canceled", groupedFile); 1174 | } 1175 | if (this.options.uploadMultiple) { 1176 | this.emit("canceledmultiple", groupedFiles); 1177 | } 1178 | } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) { 1179 | file.status = Dropzone.CANCELED; 1180 | this.emit("canceled", file); 1181 | if (this.options.uploadMultiple) { 1182 | this.emit("canceledmultiple", [file]); 1183 | } 1184 | } 1185 | if (this.options.autoProcessQueue) { 1186 | return this.processQueue(); 1187 | } 1188 | }; 1189 | 1190 | resolveOption = function() { 1191 | var args, option; 1192 | option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 1193 | if (typeof option === 'function') { 1194 | return option.apply(this, args); 1195 | } 1196 | return option; 1197 | }; 1198 | 1199 | Dropzone.prototype.uploadFile = function(file) { 1200 | return this.uploadFiles([file]); 1201 | }; 1202 | 1203 | Dropzone.prototype.uploadFiles = function(files) { 1204 | var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; 1205 | xhr = new XMLHttpRequest(); 1206 | for (_i = 0, _len = files.length; _i < _len; _i++) { 1207 | file = files[_i]; 1208 | file.xhr = xhr; 1209 | } 1210 | method = resolveOption(this.options.method, files); 1211 | url = resolveOption(this.options.url, files); 1212 | xhr.open(method, url, true); 1213 | xhr.withCredentials = !!this.options.withCredentials; 1214 | response = null; 1215 | handleError = (function(_this) { 1216 | return function() { 1217 | var _j, _len1, _results; 1218 | _results = []; 1219 | for (_j = 0, _len1 = files.length; _j < _len1; _j++) { 1220 | file = files[_j]; 1221 | _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr)); 1222 | } 1223 | return _results; 1224 | }; 1225 | })(this); 1226 | updateProgress = (function(_this) { 1227 | return function(e) { 1228 | var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results; 1229 | if (e != null) { 1230 | progress = 100 * e.loaded / e.total; 1231 | for (_j = 0, _len1 = files.length; _j < _len1; _j++) { 1232 | file = files[_j]; 1233 | file.upload = { 1234 | progress: progress, 1235 | total: e.total, 1236 | bytesSent: e.loaded 1237 | }; 1238 | } 1239 | } else { 1240 | allFilesFinished = true; 1241 | progress = 100; 1242 | for (_k = 0, _len2 = files.length; _k < _len2; _k++) { 1243 | file = files[_k]; 1244 | if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) { 1245 | allFilesFinished = false; 1246 | } 1247 | file.upload.progress = progress; 1248 | file.upload.bytesSent = file.upload.total; 1249 | } 1250 | if (allFilesFinished) { 1251 | return; 1252 | } 1253 | } 1254 | _results = []; 1255 | for (_l = 0, _len3 = files.length; _l < _len3; _l++) { 1256 | file = files[_l]; 1257 | _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent)); 1258 | } 1259 | return _results; 1260 | }; 1261 | })(this); 1262 | xhr.onload = (function(_this) { 1263 | return function(e) { 1264 | var _ref; 1265 | if (files[0].status === Dropzone.CANCELED) { 1266 | return; 1267 | } 1268 | if (xhr.readyState !== 4) { 1269 | return; 1270 | } 1271 | response = xhr.responseText; 1272 | if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { 1273 | try { 1274 | response = JSON.parse(response); 1275 | } catch (_error) { 1276 | e = _error; 1277 | response = "Invalid JSON response from server."; 1278 | } 1279 | } 1280 | updateProgress(); 1281 | if (!((200 <= (_ref = xhr.status) && _ref < 300))) { 1282 | return handleError(); 1283 | } else { 1284 | return _this._finished(files, response, e); 1285 | } 1286 | }; 1287 | })(this); 1288 | xhr.onerror = (function(_this) { 1289 | return function() { 1290 | if (files[0].status === Dropzone.CANCELED) { 1291 | return; 1292 | } 1293 | return handleError(); 1294 | }; 1295 | })(this); 1296 | progressObj = (_ref = xhr.upload) != null ? _ref : xhr; 1297 | progressObj.onprogress = updateProgress; 1298 | headers = { 1299 | "Accept": "application/json", 1300 | "Cache-Control": "no-cache", 1301 | "X-Requested-With": "XMLHttpRequest" 1302 | }; 1303 | if (this.options.headers) { 1304 | extend(headers, this.options.headers); 1305 | } 1306 | for (headerName in headers) { 1307 | headerValue = headers[headerName]; 1308 | xhr.setRequestHeader(headerName, headerValue); 1309 | } 1310 | formData = new FormData(); 1311 | if (this.options.params) { 1312 | _ref1 = this.options.params; 1313 | for (key in _ref1) { 1314 | value = _ref1[key]; 1315 | formData.append(key, value); 1316 | } 1317 | } 1318 | for (_j = 0, _len1 = files.length; _j < _len1; _j++) { 1319 | file = files[_j]; 1320 | this.emit("sending", file, xhr, formData); 1321 | } 1322 | if (this.options.uploadMultiple) { 1323 | this.emit("sendingmultiple", files, xhr, formData); 1324 | } 1325 | if (this.element.tagName === "FORM") { 1326 | _ref2 = this.element.querySelectorAll("input, textarea, select, button"); 1327 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { 1328 | input = _ref2[_k]; 1329 | inputName = input.getAttribute("name"); 1330 | inputType = input.getAttribute("type"); 1331 | if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { 1332 | _ref3 = input.options; 1333 | for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { 1334 | option = _ref3[_l]; 1335 | if (option.selected) { 1336 | formData.append(inputName, option.value); 1337 | } 1338 | } 1339 | } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) { 1340 | formData.append(inputName, input.value); 1341 | } 1342 | } 1343 | } 1344 | for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) { 1345 | formData.append(this._getParamName(i), files[i], files[i].name); 1346 | } 1347 | return xhr.send(formData); 1348 | }; 1349 | 1350 | Dropzone.prototype._finished = function(files, responseText, e) { 1351 | var file, _i, _len; 1352 | for (_i = 0, _len = files.length; _i < _len; _i++) { 1353 | file = files[_i]; 1354 | file.status = Dropzone.SUCCESS; 1355 | this.emit("success", file, responseText, e); 1356 | this.emit("complete", file); 1357 | } 1358 | if (this.options.uploadMultiple) { 1359 | this.emit("successmultiple", files, responseText, e); 1360 | this.emit("completemultiple", files); 1361 | } 1362 | if (this.options.autoProcessQueue) { 1363 | return this.processQueue(); 1364 | } 1365 | }; 1366 | 1367 | Dropzone.prototype._errorProcessing = function(files, message, xhr) { 1368 | var file, _i, _len; 1369 | for (_i = 0, _len = files.length; _i < _len; _i++) { 1370 | file = files[_i]; 1371 | file.status = Dropzone.ERROR; 1372 | this.emit("error", file, message, xhr); 1373 | this.emit("complete", file); 1374 | } 1375 | if (this.options.uploadMultiple) { 1376 | this.emit("errormultiple", files, message, xhr); 1377 | this.emit("completemultiple", files); 1378 | } 1379 | if (this.options.autoProcessQueue) { 1380 | return this.processQueue(); 1381 | } 1382 | }; 1383 | 1384 | return Dropzone; 1385 | 1386 | })(Emitter); 1387 | 1388 | Dropzone.version = "4.0.1"; 1389 | 1390 | Dropzone.options = {}; 1391 | 1392 | Dropzone.optionsForElement = function(element) { 1393 | if (element.getAttribute("id")) { 1394 | return Dropzone.options[camelize(element.getAttribute("id"))]; 1395 | } else { 1396 | return void 0; 1397 | } 1398 | }; 1399 | 1400 | Dropzone.instances = []; 1401 | 1402 | Dropzone.forElement = function(element) { 1403 | if (typeof element === "string") { 1404 | element = document.querySelector(element); 1405 | } 1406 | if ((element != null ? element.dropzone : void 0) == null) { 1407 | 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."); 1408 | } 1409 | return element.dropzone; 1410 | }; 1411 | 1412 | Dropzone.autoDiscover = true; 1413 | 1414 | Dropzone.discover = function() { 1415 | var checkElements, dropzone, dropzones, _i, _len, _results; 1416 | if (document.querySelectorAll) { 1417 | dropzones = document.querySelectorAll(".dropzone"); 1418 | } else { 1419 | dropzones = []; 1420 | checkElements = function(elements) { 1421 | var el, _i, _len, _results; 1422 | _results = []; 1423 | for (_i = 0, _len = elements.length; _i < _len; _i++) { 1424 | el = elements[_i]; 1425 | if (/(^| )dropzone($| )/.test(el.className)) { 1426 | _results.push(dropzones.push(el)); 1427 | } else { 1428 | _results.push(void 0); 1429 | } 1430 | } 1431 | return _results; 1432 | }; 1433 | checkElements(document.getElementsByTagName("div")); 1434 | checkElements(document.getElementsByTagName("form")); 1435 | } 1436 | _results = []; 1437 | for (_i = 0, _len = dropzones.length; _i < _len; _i++) { 1438 | dropzone = dropzones[_i]; 1439 | if (Dropzone.optionsForElement(dropzone) !== false) { 1440 | _results.push(new Dropzone(dropzone)); 1441 | } else { 1442 | _results.push(void 0); 1443 | } 1444 | } 1445 | return _results; 1446 | }; 1447 | 1448 | Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i]; 1449 | 1450 | Dropzone.isBrowserSupported = function() { 1451 | var capableBrowser, regex, _i, _len, _ref; 1452 | capableBrowser = true; 1453 | if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { 1454 | if (!("classList" in document.createElement("a"))) { 1455 | capableBrowser = false; 1456 | } else { 1457 | _ref = Dropzone.blacklistedBrowsers; 1458 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1459 | regex = _ref[_i]; 1460 | if (regex.test(navigator.userAgent)) { 1461 | capableBrowser = false; 1462 | continue; 1463 | } 1464 | } 1465 | } 1466 | } else { 1467 | capableBrowser = false; 1468 | } 1469 | return capableBrowser; 1470 | }; 1471 | 1472 | without = function(list, rejectedItem) { 1473 | var item, _i, _len, _results; 1474 | _results = []; 1475 | for (_i = 0, _len = list.length; _i < _len; _i++) { 1476 | item = list[_i]; 1477 | if (item !== rejectedItem) { 1478 | _results.push(item); 1479 | } 1480 | } 1481 | return _results; 1482 | }; 1483 | 1484 | camelize = function(str) { 1485 | return str.replace(/[\-_](\w)/g, function(match) { 1486 | return match.charAt(1).toUpperCase(); 1487 | }); 1488 | }; 1489 | 1490 | Dropzone.createElement = function(string) { 1491 | var div; 1492 | div = document.createElement("div"); 1493 | div.innerHTML = string; 1494 | return div.childNodes[0]; 1495 | }; 1496 | 1497 | Dropzone.elementInside = function(element, container) { 1498 | if (element === container) { 1499 | return true; 1500 | } 1501 | while (element = element.parentNode) { 1502 | if (element === container) { 1503 | return true; 1504 | } 1505 | } 1506 | return false; 1507 | }; 1508 | 1509 | Dropzone.getElement = function(el, name) { 1510 | var element; 1511 | if (typeof el === "string") { 1512 | element = document.querySelector(el); 1513 | } else if (el.nodeType != null) { 1514 | element = el; 1515 | } 1516 | if (element == null) { 1517 | throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); 1518 | } 1519 | return element; 1520 | }; 1521 | 1522 | Dropzone.getElements = function(els, name) { 1523 | var e, el, elements, _i, _j, _len, _len1, _ref; 1524 | if (els instanceof Array) { 1525 | elements = []; 1526 | try { 1527 | for (_i = 0, _len = els.length; _i < _len; _i++) { 1528 | el = els[_i]; 1529 | elements.push(this.getElement(el, name)); 1530 | } 1531 | } catch (_error) { 1532 | e = _error; 1533 | elements = null; 1534 | } 1535 | } else if (typeof els === "string") { 1536 | elements = []; 1537 | _ref = document.querySelectorAll(els); 1538 | for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { 1539 | el = _ref[_j]; 1540 | elements.push(el); 1541 | } 1542 | } else if (els.nodeType != null) { 1543 | elements = [els]; 1544 | } 1545 | if (!((elements != null) && elements.length)) { 1546 | throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); 1547 | } 1548 | return elements; 1549 | }; 1550 | 1551 | Dropzone.confirm = function(question, accepted, rejected) { 1552 | if (window.confirm(question)) { 1553 | return accepted(); 1554 | } else if (rejected != null) { 1555 | return rejected(); 1556 | } 1557 | }; 1558 | 1559 | Dropzone.isValidFile = function(file, acceptedFiles) { 1560 | var baseMimeType, mimeType, validType, _i, _len; 1561 | if (!acceptedFiles) { 1562 | return true; 1563 | } 1564 | acceptedFiles = acceptedFiles.split(","); 1565 | mimeType = file.type; 1566 | baseMimeType = mimeType.replace(/\/.*$/, ""); 1567 | for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) { 1568 | validType = acceptedFiles[_i]; 1569 | validType = validType.trim(); 1570 | if (validType.charAt(0) === ".") { 1571 | if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { 1572 | return true; 1573 | } 1574 | } else if (/\/\*$/.test(validType)) { 1575 | if (baseMimeType === validType.replace(/\/.*$/, "")) { 1576 | return true; 1577 | } 1578 | } else { 1579 | if (mimeType === validType) { 1580 | return true; 1581 | } 1582 | } 1583 | } 1584 | return false; 1585 | }; 1586 | 1587 | if (typeof jQuery !== "undefined" && jQuery !== null) { 1588 | jQuery.fn.dropzone = function(options) { 1589 | return this.each(function() { 1590 | return new Dropzone(this, options); 1591 | }); 1592 | }; 1593 | } 1594 | 1595 | if (typeof module !== "undefined" && module !== null) { 1596 | module.exports = Dropzone; 1597 | } else { 1598 | window.Dropzone = Dropzone; 1599 | } 1600 | 1601 | Dropzone.ADDED = "added"; 1602 | 1603 | Dropzone.QUEUED = "queued"; 1604 | 1605 | Dropzone.ACCEPTED = Dropzone.QUEUED; 1606 | 1607 | Dropzone.UPLOADING = "uploading"; 1608 | 1609 | Dropzone.PROCESSING = Dropzone.UPLOADING; 1610 | 1611 | Dropzone.CANCELED = "canceled"; 1612 | 1613 | Dropzone.ERROR = "error"; 1614 | 1615 | Dropzone.SUCCESS = "success"; 1616 | 1617 | 1618 | /* 1619 | 1620 | Bugfix for iOS 6 and 7 1621 | Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios 1622 | based on the work of https://github.com/stomita/ios-imagefile-megapixel 1623 | */ 1624 | 1625 | detectVerticalSquash = function(img) { 1626 | var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy; 1627 | iw = img.naturalWidth; 1628 | ih = img.naturalHeight; 1629 | canvas = document.createElement("canvas"); 1630 | canvas.width = 1; 1631 | canvas.height = ih; 1632 | ctx = canvas.getContext("2d"); 1633 | ctx.drawImage(img, 0, 0); 1634 | data = ctx.getImageData(0, 0, 1, ih).data; 1635 | sy = 0; 1636 | ey = ih; 1637 | py = ih; 1638 | while (py > sy) { 1639 | alpha = data[(py - 1) * 4 + 3]; 1640 | if (alpha === 0) { 1641 | ey = py; 1642 | } else { 1643 | sy = py; 1644 | } 1645 | py = (ey + sy) >> 1; 1646 | } 1647 | ratio = py / ih; 1648 | if (ratio === 0) { 1649 | return 1; 1650 | } else { 1651 | return ratio; 1652 | } 1653 | }; 1654 | 1655 | drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { 1656 | var vertSquashRatio; 1657 | vertSquashRatio = detectVerticalSquash(img); 1658 | return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); 1659 | }; 1660 | 1661 | 1662 | /* 1663 | * contentloaded.js 1664 | * 1665 | * Author: Diego Perini (diego.perini at gmail.com) 1666 | * Summary: cross-browser wrapper for DOMContentLoaded 1667 | * Updated: 20101020 1668 | * License: MIT 1669 | * Version: 1.2 1670 | * 1671 | * URL: 1672 | * http://javascript.nwbox.com/ContentLoaded/ 1673 | * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE 1674 | */ 1675 | 1676 | contentLoaded = function(win, fn) { 1677 | var add, doc, done, init, poll, pre, rem, root, top; 1678 | done = false; 1679 | top = true; 1680 | doc = win.document; 1681 | root = doc.documentElement; 1682 | add = (doc.addEventListener ? "addEventListener" : "attachEvent"); 1683 | rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); 1684 | pre = (doc.addEventListener ? "" : "on"); 1685 | init = function(e) { 1686 | if (e.type === "readystatechange" && doc.readyState !== "complete") { 1687 | return; 1688 | } 1689 | (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); 1690 | if (!done && (done = true)) { 1691 | return fn.call(win, e.type || e); 1692 | } 1693 | }; 1694 | poll = function() { 1695 | var e; 1696 | try { 1697 | root.doScroll("left"); 1698 | } catch (_error) { 1699 | e = _error; 1700 | setTimeout(poll, 50); 1701 | return; 1702 | } 1703 | return init("poll"); 1704 | }; 1705 | if (doc.readyState !== "complete") { 1706 | if (doc.createEventObject && root.doScroll) { 1707 | try { 1708 | top = !win.frameElement; 1709 | } catch (_error) {} 1710 | if (top) { 1711 | poll(); 1712 | } 1713 | } 1714 | doc[add](pre + "DOMContentLoaded", init, false); 1715 | doc[add](pre + "readystatechange", init, false); 1716 | return win[add](pre + "load", init, false); 1717 | } 1718 | }; 1719 | 1720 | Dropzone._autoDiscoverFunction = function() { 1721 | if (Dropzone.autoDiscover) { 1722 | return Dropzone.discover(); 1723 | } 1724 | }; 1725 | 1726 | contentLoaded(window, Dropzone._autoDiscoverFunction); 1727 | 1728 | }).call(this); --------------------------------------------------------------------------------