├── .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 |
{!! trans('laradrop::app.addFolder') !!}
4 |
{!! trans('laradrop::app.uploadFiles') !!}
5 |
{!! trans('laradrop::app.startUpload') !!}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Jasekz/Laradrop/resources/views/previewContainer.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
14 |
15 |
16 | Cancel
17 |
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 |
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 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 = "";
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);
--------------------------------------------------------------------------------