├── .gitignore
├── Gruntfile.js
├── README.md
├── bower.json
├── demo
├── index.html
└── upload.php
├── dropper.jquery.json
├── jquery.fs.dropper.css
├── jquery.fs.dropper.js
├── jquery.fs.dropper.min.css
├── jquery.fs.dropper.min.js
├── package.json
└── src
├── jquery.fs.dropper-config.less
├── jquery.fs.dropper-styles.less
├── jquery.fs.dropper.js
└── jquery.fs.dropper.less
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*global module:false*/
2 |
3 | // Less
4 |
5 | module.exports = function(grunt) {
6 |
7 | grunt.initConfig({
8 | pkg: grunt.file.readJSON('package.json'),
9 | meta: {
10 | banner: '/* \n' +
11 | ' * <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n' +
12 | ' * <%= pkg.description %> \n' +
13 | ' * <%= pkg.homepage %> \n' +
14 | ' * \n' +
15 | ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>; <%= pkg.license %> Licensed \n' +
16 | ' */\n'
17 | },
18 | // JS Hint
19 | jshint: {
20 | options: {
21 | globals: {
22 | 'jQuery': true,
23 | '$' : true
24 | },
25 | browser: true,
26 | curly: true,
27 | eqeqeq: true,
28 | forin: true,
29 | freeze: true,
30 | immed: true,
31 | latedef: true,
32 | newcap: true,
33 | noarg: true,
34 | nonew: true,
35 | smarttabs: true,
36 | sub: true,
37 | undef: true,
38 | validthis: true
39 | },
40 | files: [
41 | 'src/<%= pkg.codename %>.js'
42 | ]
43 | },
44 | // Copy
45 | copy: {
46 | main: {
47 | files: [
48 | {
49 | src: 'src/<%= pkg.codename %>.js',
50 | dest: '<%= pkg.codename %>.js'
51 | }
52 | ]
53 | }
54 | },
55 | // Uglify
56 | uglify: {
57 | options: {
58 | report: 'min'
59 | },
60 | target: {
61 | files: {
62 | '<%= pkg.codename %>.min.js': [ '<%= pkg.codename %>.js' ]
63 | }
64 | }
65 | },
66 | // jQuery Manifest
67 | jquerymanifest: {
68 | options: {
69 | source: grunt.file.readJSON('package.json'),
70 | overrides: {
71 | name: '<%= pkg.id %>',
72 | keywords: '<%= pkg.keywords %>',
73 | homepage: '<%= pkg.homepage %>',
74 | docs: '<%= pkg.homepage %>',
75 | demo: '<%= pkg.homepage %>',
76 | download: '<%= pkg.repository.url %>',
77 | bugs: '<%= pkg.repository.url %>/issues',
78 | dependencies: {
79 | jquery: '>=1.7'
80 | }
81 | }
82 | }
83 | },
84 | // LESS
85 | less: {
86 | main: {
87 | files: {
88 | '<%= pkg.codename %>.css': 'src/<%= pkg.codename %>.less'
89 | }
90 | },
91 | min: {
92 | options: {
93 | report: 'min',
94 | cleancss: true
95 | },
96 | files: {
97 | '<%= pkg.codename %>.min.css': 'src/<%= pkg.codename %>.less'
98 | }
99 | }
100 | },
101 | // Auto Prefixer
102 | autoprefixer: {
103 | options: {
104 | borwsers: [ '> 1%', 'last 5 versions', 'Firefox ESR', 'Opera 12.1', '>= ie 8' ]
105 | },
106 | no_dest: {
107 | src: '*.css'
108 | }
109 | },
110 | // Banner
111 | usebanner: {
112 | options: {
113 | position: 'top',
114 | banner: '<%= meta.banner %>'
115 | },
116 | files: {
117 | src: [
118 | '<%= pkg.codename %>.css',
119 | '<%= pkg.codename %>.js',
120 | '<%= pkg.codename %>.min.css',
121 | '<%= pkg.codename %>.min.js'
122 | ]
123 | }
124 | },
125 | //Bower sync
126 | sync: {
127 | all: {
128 | options: {
129 | sync: [ 'name', 'version', 'description', 'author', 'license', 'homepage' ],
130 | overrides: {
131 | main: [
132 | '<%= pkg.codename %>.js',
133 | '<%= pkg.codename %>.css'
134 | ],
135 | ignore: [ "*.jquery.json" ]
136 | }
137 | }
138 | }
139 | }
140 | });
141 |
142 | // Load tasks
143 | grunt.loadNpmTasks('grunt-contrib-jshint');
144 | grunt.loadNpmTasks('grunt-contrib-copy');
145 | grunt.loadNpmTasks('grunt-contrib-uglify');
146 | grunt.loadNpmTasks('grunt-jquerymanifest');
147 | grunt.loadNpmTasks('grunt-contrib-less');
148 | grunt.loadNpmTasks('grunt-autoprefixer');
149 | grunt.loadNpmTasks('grunt-banner');
150 | grunt.loadNpmTasks('grunt-npm2bower-sync');
151 |
152 | // Readme
153 | grunt.registerTask('buildReadme', 'Build Formstone README.md file.', function () {
154 | var pkg = grunt.file.readJSON('package.json'),
155 | extra = grunt.file.exists('src/README.md') ? '\n\n---\n\n' + grunt.file.read('src/README.md') : '';
156 | destination = "README.md",
157 | markdown = '
Development of this plugin has ended. Please upgrade to the new Formstone.
\n\n' +
158 | '
\n' +
159 | '# ' + pkg.name + ' \n\n' +
160 | pkg.description + ' \n\n' +
161 | '- [Demo](' + pkg.demo + ') \n' +
162 | '- [Documentation](' + pkg.homepage + ') \n\n' +
163 | '#### Bower Support \n' +
164 | '`bower install ' + pkg.name + '` ' +
165 | extra;
166 |
167 | grunt.file.write(destination, markdown);
168 | grunt.log.writeln('File "' + destination + '" created.');
169 | });
170 |
171 | // Default task.
172 | grunt.registerTask('default', [ 'jshint', 'copy', 'uglify', 'jquerymanifest', 'less', 'autoprefixer', 'usebanner', 'sync', 'buildReadme' ]);
173 |
174 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Development of this plugin has ended. Please upgrade to the new Formstone.
2 |
3 |
4 | # Dropper
5 |
6 | A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.
7 |
8 | - [Demo](http://classic.formstone.it/components/Dropper/demo/index.html)
9 | - [Documentation](http://classic.formstone.it/dropper/)
10 |
11 | #### Bower Support
12 | `bower install Dropper`
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dropper",
3 | "version": "1.0.1",
4 | "description": "A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.",
5 | "author": {
6 | "name": "Ben Plum",
7 | "email": "mr@benplum.com",
8 | "url": "http://www.benplum.com"
9 | },
10 | "license": "MIT",
11 | "homepage": "http://classic.formstone.it/dropper/",
12 | "main": [
13 | "jquery.fs.dropper.js",
14 | "jquery.fs.dropper.css"
15 | ],
16 | "ignore": [
17 | "*.jquery.json"
18 | ]
19 | }
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Dropper Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
44 |
45 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
134 |
135 |
136 |
141 |
142 |
143 |
144 |
145 |
Basic
146 |
Dropper will create a simple 'drop zone' for file uploads:
147 |
148 |
$(".target").dropper({
149 | action: "upload.php"
150 | });
151 |
<div class="target"></div>
152 |
153 |
Demo
154 |
166 |
167 |
168 |
Uploads
169 |
Dropper does not store or manipulate uploaded files on the server, it simply facilitates the asynchronous upload process from the front end.
170 |
171 |
172 |
173 |
174 |
175 |
176 |
181 |
182 |
--------------------------------------------------------------------------------
/demo/upload.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | // Remember to do something with your uploads!
4 |
5 | $f = $_FILES["file"];
6 | $file = $f["name"];
7 |
8 | $error = false;
9 |
10 | if ($error) {
11 | die("ERROR: " . $error);
12 | } else {
13 | die($file);
14 | }
15 |
16 | ?>
--------------------------------------------------------------------------------
/dropper.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropper",
3 | "version": "1.0.1",
4 | "title": "Dropper",
5 | "author": {
6 | "name": "Ben Plum",
7 | "email": "mr@benplum.com",
8 | "url": "http://www.benplum.com"
9 | },
10 | "licenses": [
11 | {
12 | "type": "MIT",
13 | "url": "http://opensource.org/licenses/MIT"
14 | }
15 | ],
16 | "dependencies": {
17 | "jquery": ">=1.7"
18 | },
19 | "description": "A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.",
20 | "keywords": [
21 | "upload",
22 | "form",
23 | "ajax",
24 | "javascript",
25 | "jquery",
26 | "formstone"
27 | ],
28 | "docs": "http://classic.formstone.it/dropper/",
29 | "demo": "http://classic.formstone.it/dropper/",
30 | "download": "https://github.com/FormstoneClassic/Dropper",
31 | "bugs": "https://github.com/FormstoneClassic/Dropper/issues",
32 | "homepage": "http://classic.formstone.it/dropper/"
33 | }
--------------------------------------------------------------------------------
/jquery.fs.dropper.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Dropper v1.0.1 - 2015-04-04
3 | * A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.
4 | * http://classic.formstone.it/dropper/
5 | *
6 | * Copyright 2015 Ben Plum; MIT Licensed
7 | */
8 |
9 | .dropper {
10 | overflow: hidden;
11 | }
12 | .dropper,
13 | .dropper *,
14 | .dropper *:before,
15 | .dropper *:after {
16 | box-sizing: border-box;
17 | }
18 | .dropper-dropzone {
19 | background: #ffffff;
20 | border: 3px dashed #cccccc;
21 | border-radius: 0;
22 | color: #666666;
23 | cursor: pointer;
24 | font-size: 14px;
25 | margin: 0;
26 | padding: 25px;
27 | text-align: center;
28 | }
29 | .dropper.dropping .dropper-dropzone,
30 | .no-touch .dropper:hover .dropper-dropzone {
31 | background: #eeeeee;
32 | border-color: #999999;
33 | color: #333333;
34 | }
35 | .dropper-input {
36 | position: absolute;
37 | left: 100%;
38 | opacity: 0;
39 | }
40 | .no-opacity .dropper-input {
41 | left: -999px;
42 | }
43 |
--------------------------------------------------------------------------------
/jquery.fs.dropper.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Dropper v1.0.1 - 2015-04-04
3 | * A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.
4 | * http://classic.formstone.it/dropper/
5 | *
6 | * Copyright 2015 Ben Plum; MIT Licensed
7 | */
8 |
9 | ;(function ($, window) {
10 | "use strict";
11 |
12 | var supported = (window.File && window.FileReader && window.FileList);
13 |
14 | /**
15 | * @options
16 | * @param action [string] "Where to submit uploads"
17 | * @param label [string] <'Drag and drop files or click to select'> "Dropzone text"
18 | * @param maxQueue [int] <2> "Number of files to simultaneously upload"
19 | * @param maxSize [int] <5242880> "Max file size allowed"
20 | * @param postData [object] "Extra data to post with upload"
21 | * @param postKey [string] <'file'> "Key to upload file as"
22 | */
23 |
24 | var options = {
25 | action: "",
26 | label: "Drag and drop files or click to select",
27 | maxQueue: 2,
28 | maxSize: 5242880, // 5 mb
29 | postData: {},
30 | postKey: "file"
31 | };
32 |
33 | /**
34 | * @events
35 | * @event start.dropper ""
36 | * @event complete.dropper ""
37 | * @event fileStart.dropper ""
38 | * @event fileProgress.dropper ""
39 | * @event fileComplete.dropper ""
40 | * @event fileError.dropper ""
41 | */
42 |
43 | var pub = {
44 |
45 | /**
46 | * @method
47 | * @name defaults
48 | * @description Sets default plugin options
49 | * @param opts [object] <{}> "Options object"
50 | * @example $.dropper("defaults", opts);
51 | */
52 | defaults: function(opts) {
53 | options = $.extend(options, opts || {});
54 |
55 | return (typeof this === 'object') ? $(this) : true;
56 | }
57 | };
58 |
59 | /**
60 | * @method private
61 | * @name _init
62 | * @description Initializes plugin
63 | * @param opts [object] "Initialization options"
64 | */
65 | function _init(opts) {
66 | var $items = $(this);
67 |
68 | if (supported) {
69 | // Settings
70 | opts = $.extend({}, options, opts);
71 |
72 | // Apply to each element
73 | for (var i = 0, count = $items.length; i < count; i++) {
74 | _build($items.eq(i), opts);
75 | }
76 | }
77 |
78 | return $items;
79 | }
80 |
81 | /**
82 | * @method private
83 | * @name _build
84 | * @description Builds each instance
85 | * @param $nav [jQuery object] "Target jQuery object"
86 | * @param opts [object] <{}> "Options object"
87 | */
88 | function _build($dropper, opts) {
89 | opts = $.extend({}, opts, $dropper.data("dropper-options"));
90 |
91 | var html = "";
92 |
93 | html += '';
94 | html += opts.label;
95 | html += '
';
96 | html += ' 1) {
98 | html += ' multiple';
99 | }
100 | html += '>';
101 |
102 | $dropper.addClass("dropper")
103 | .append(html);
104 |
105 | var data = $.extend({
106 | $dropper: $dropper,
107 | $input: $dropper.find(".dropper-input"),
108 | queue: [],
109 | total: 0,
110 | uploading: false
111 | }, opts);
112 |
113 | $dropper.on("click.dropper", ".dropper-dropzone", data, _onClick)
114 | .on("dragenter.dropper", data, _onDragEnter)
115 | .on("dragover.dropper", data, _onDragOver)
116 | .on("dragleave.dropper", data, _onDragOut)
117 | .on("drop.dropper", ".dropper-dropzone", data, _onDrop)
118 | .data("dropper", data);
119 |
120 | data.$input.on("change.dropper", data, _onChange);
121 | }
122 |
123 | /**
124 | * @method private
125 | * @name _onClick
126 | * @description Handles click to dropzone
127 | * @param e [object] "Event data"
128 | */
129 | function _onClick(e) {
130 | e.stopPropagation();
131 | e.preventDefault();
132 |
133 | var data = e.data;
134 |
135 | data.$input.trigger("click");
136 | }
137 |
138 | /**
139 | * @method private
140 | * @name _onChange
141 | * @description Handles change to hidden input
142 | * @param e [object] "Event data"
143 | */
144 | function _onChange(e) {
145 | e.stopPropagation();
146 | e.preventDefault();
147 |
148 | var data = e.data,
149 | files = data.$input[0].files;
150 |
151 | if (files.length) {
152 | _handleUpload(data, files);
153 | }
154 | }
155 |
156 | /**
157 | * @method private
158 | * @name _onDragEnter
159 | * @description Handles dragenter to dropzone
160 | * @param e [object] "Event data"
161 | */
162 | function _onDragEnter(e) {
163 | e.stopPropagation();
164 | e.preventDefault();
165 |
166 | var data = e.data;
167 |
168 | data.$dropper.addClass("dropping");
169 | }
170 |
171 | /**
172 | * @method private
173 | * @name _onDragOver
174 | * @description Handles dragover to dropzone
175 | * @param e [object] "Event data"
176 | */
177 | function _onDragOver(e) {
178 | e.stopPropagation();
179 | e.preventDefault();
180 |
181 | var data = e.data;
182 |
183 | data.$dropper.addClass("dropping");
184 | }
185 |
186 | /**
187 | * @method private
188 | * @name _onDragOut
189 | * @description Handles dragout to dropzone
190 | * @param e [object] "Event data"
191 | */
192 | function _onDragOut(e) {
193 | e.stopPropagation();
194 | e.preventDefault();
195 |
196 | var data = e.data;
197 |
198 | data.$dropper.removeClass("dropping");
199 | }
200 |
201 | /**
202 | * @method private
203 | * @name _onDrop
204 | * @description Handles drop to dropzone
205 | * @param e [object] "Event data"
206 | */
207 | function _onDrop(e) {
208 | e.preventDefault();
209 |
210 | var data = e.data,
211 | files = e.originalEvent.dataTransfer.files;
212 |
213 | data.$dropper.removeClass("dropping");
214 |
215 | _handleUpload(data, files);
216 | }
217 |
218 | /**
219 | * @method private
220 | * @name _handleUpload
221 | * @description Handles new files
222 | * @param data [object] "Instance data"
223 | * @param files [object] "File list"
224 | */
225 | function _handleUpload(data, files) {
226 | var newFiles = [];
227 |
228 | for (var i = 0; i < files.length; i++) {
229 | var file = {
230 | index: data.total++,
231 | file: files[i],
232 | name: files[i].name,
233 | size: files[i].size,
234 | started: false,
235 | complete: false,
236 | error: false,
237 | transfer: null
238 | };
239 |
240 | newFiles.push(file);
241 | data.queue.push(file);
242 | }
243 |
244 | if (!data.uploading) {
245 | $(window).on("beforeunload.dropper", function(){
246 | return 'You have uploads pending, are you sure you want to leave this page?';
247 | });
248 |
249 | data.uploading = true;
250 | }
251 |
252 | data.$dropper.trigger("start.dropper", [ newFiles ]);
253 |
254 | _checkQueue(data);
255 | }
256 |
257 | /**
258 | * @method private
259 | * @name _checkQueue
260 | * @description Checks and updates file queue
261 | * @param data [object] "Instance data"
262 | */
263 | function _checkQueue(data) {
264 | var transfering = 0,
265 | newQueue = [];
266 |
267 | // remove lingering items from queue
268 | for (var i in data.queue) {
269 | if (data.queue.hasOwnProperty(i) && !data.queue[i].complete && !data.queue[i].error) {
270 | newQueue.push(data.queue[i]);
271 | }
272 | }
273 |
274 | data.queue = newQueue;
275 |
276 | for (var j in data.queue) {
277 | if (data.queue.hasOwnProperty(j)) {
278 | if (!data.queue[j].started) {
279 | var formData = new FormData();
280 |
281 | formData.append(data.postKey, data.queue[j].file);
282 |
283 | for (var k in data.postData) {
284 | if (data.postData.hasOwnProperty(k)) {
285 | formData.append(k, data.postData[k]);
286 | }
287 | }
288 |
289 | _uploadFile(data, data.queue[j], formData);
290 | }
291 |
292 | transfering++;
293 |
294 | if (transfering >= data.maxQueue) {
295 | return;
296 | } else {
297 | i++;
298 | }
299 | }
300 | }
301 |
302 | if (transfering === 0) {
303 | $(window).off("beforeunload.dropper");
304 |
305 | data.uploading = false;
306 |
307 | data.$dropper.trigger("complete.dropper");
308 | }
309 | }
310 |
311 | /**
312 | * @method private
313 | * @name _uploadFile
314 | * @description Uploads file
315 | * @param data [object] "Instance data"
316 | * @param file [object] "Target file"
317 | * @param formData [object] "Target form"
318 | */
319 | function _uploadFile(data, file, formData) {
320 | if (file.size >= data.maxSize) {
321 | file.error = true;
322 | data.$dropper.trigger("fileError.dropper", [ file, "Too large" ]);
323 |
324 | _checkQueue(data);
325 | } else {
326 | file.started = true;
327 | file.transfer = $.ajax({
328 | url: data.action,
329 | data: formData,
330 | type: "POST",
331 | contentType:false,
332 | processData: false,
333 | cache: false,
334 | xhr: function() {
335 | var $xhr = $.ajaxSettings.xhr();
336 |
337 | if ($xhr.upload) {
338 | $xhr.upload.addEventListener("progress", function(e) {
339 | var percent = 0,
340 | position = e.loaded || e.position,
341 | total = e.total;
342 |
343 | if (e.lengthComputable) {
344 | percent = Math.ceil(position / total * 100);
345 | }
346 |
347 | data.$dropper.trigger("fileProgress.dropper", [ file, percent ]);
348 | }, false);
349 | }
350 |
351 | return $xhr;
352 | },
353 | beforeSend: function(e) {
354 | data.$dropper.trigger("fileStart.dropper", [ file ]);
355 | },
356 | success: function(response, status, jqXHR) {
357 | file.complete = true;
358 | data.$dropper.trigger("fileComplete.dropper", [ file, response ]);
359 |
360 | _checkQueue(data);
361 | },
362 | error: function(jqXHR, status, error) {
363 | file.error = true;
364 | data.$dropper.trigger("fileError.dropper", [ file, error ]);
365 |
366 | _checkQueue(data);
367 | }
368 | });
369 | }
370 | }
371 |
372 | $.fn.dropper = function(method) {
373 | if (pub[method]) {
374 | return pub[method].apply(this, Array.prototype.slice.call(arguments, 1));
375 | } else if (typeof method === 'object' || !method) {
376 | return _init.apply(this, arguments);
377 | }
378 | return this;
379 | };
380 |
381 | $.dropper = function(method) {
382 | if (method === "defaults") {
383 | pub.defaults.apply(this, Array.prototype.slice.call(arguments, 1));
384 | }
385 | };
386 | })(jQuery, window);
--------------------------------------------------------------------------------
/jquery.fs.dropper.min.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Dropper v1.0.1 - 2015-04-04
3 | * A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.
4 | * http://classic.formstone.it/dropper/
5 | *
6 | * Copyright 2015 Ben Plum; MIT Licensed
7 | */
8 |
9 | .dropper{overflow:hidden}.dropper,.dropper *,.dropper :before,.dropper :after{box-sizing:border-box}.dropper-dropzone{background:#fff;border:3px dashed #ccc;border-radius:0;color:#666;cursor:pointer;font-size:14px;margin:0;padding:25px;text-align:center}.dropper.dropping .dropper-dropzone,.no-touch .dropper:hover .dropper-dropzone{background:#eee;border-color:#999;color:#333}.dropper-input{position:absolute;left:100%;opacity:0}.no-opacity .dropper-input{left:-999px}
--------------------------------------------------------------------------------
/jquery.fs.dropper.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Dropper v1.0.1 - 2015-04-04
3 | * A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.
4 | * http://classic.formstone.it/dropper/
5 | *
6 | * Copyright 2015 Ben Plum; MIT Licensed
7 | */
8 |
9 | !function(a,b){"use strict";function c(b){var c=a(this);if(n){b=a.extend({},o,b);for(var e=0,f=c.length;f>e;e++)d(c.eq(e),b)}return c}function d(b,c){c=a.extend({},c,b.data("dropper-options"));var d="";d+='',d+=c.label,d+="
",d+='1&&(d+=" multiple"),d+=">",b.addClass("dropper").append(d);var k=a.extend({$dropper:b,$input:b.find(".dropper-input"),queue:[],total:0,uploading:!1},c);b.on("click.dropper",".dropper-dropzone",k,e).on("dragenter.dropper",k,g).on("dragover.dropper",k,h).on("dragleave.dropper",k,i).on("drop.dropper",".dropper-dropzone",k,j).data("dropper",k),k.$input.on("change.dropper",k,f)}function e(a){a.stopPropagation(),a.preventDefault();var b=a.data;b.$input.trigger("click")}function f(a){a.stopPropagation(),a.preventDefault();var b=a.data,c=b.$input[0].files;c.length&&k(b,c)}function g(a){a.stopPropagation(),a.preventDefault();var b=a.data;b.$dropper.addClass("dropping")}function h(a){a.stopPropagation(),a.preventDefault();var b=a.data;b.$dropper.addClass("dropping")}function i(a){a.stopPropagation(),a.preventDefault();var b=a.data;b.$dropper.removeClass("dropping")}function j(a){a.preventDefault();var b=a.data,c=a.originalEvent.dataTransfer.files;b.$dropper.removeClass("dropping"),k(b,c)}function k(c,d){for(var e=[],f=0;f=c.maxQueue)return;f++}0===d&&(a(b).off("beforeunload.dropper"),c.uploading=!1,c.$dropper.trigger("complete.dropper"))}function m(b,c,d){c.size>=b.maxSize?(c.error=!0,b.$dropper.trigger("fileError.dropper",[c,"Too large"]),l(b)):(c.started=!0,c.transfer=a.ajax({url:b.action,data:d,type:"POST",contentType:!1,processData:!1,cache:!1,xhr:function(){var d=a.ajaxSettings.xhr();return d.upload&&d.upload.addEventListener("progress",function(a){var d=0,e=a.loaded||a.position,f=a.total;a.lengthComputable&&(d=Math.ceil(e/f*100)),b.$dropper.trigger("fileProgress.dropper",[c,d])},!1),d},beforeSend:function(a){b.$dropper.trigger("fileStart.dropper",[c])},success:function(a,d,e){c.complete=!0,b.$dropper.trigger("fileComplete.dropper",[c,a]),l(b)},error:function(a,d,e){c.error=!0,b.$dropper.trigger("fileError.dropper",[c,e]),l(b)}}))}var n=b.File&&b.FileReader&&b.FileList,o={action:"",label:"Drag and drop files or click to select",maxQueue:2,maxSize:5242880,postData:{},postKey:"file"},p={defaults:function(b){return o=a.extend(o,b||{}),"object"==typeof this?a(this):!0}};a.fn.dropper=function(a){return p[a]?p[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?this:c.apply(this,arguments)},a.dropper=function(a){"defaults"===a&&p.defaults.apply(this,Array.prototype.slice.call(arguments,1))}}(jQuery,window);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dropper",
3 | "id": "dropper",
4 | "codename": "jquery.fs.dropper",
5 | "version": "1.0.1",
6 | "description": "A jQuery plugin for simple drag and drop uploads. Part of the Formstone Library.",
7 | "keywords": [
8 | "upload",
9 | "form",
10 | "ajax",
11 | "javascript",
12 | "jquery",
13 | "formstone"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/FormstoneClassic/Dropper"
18 | },
19 | "homepage": "http://classic.formstone.it/dropper/",
20 | "demo": "http://classic.formstone.it/components/Dropper/demo/index.html",
21 | "license": "MIT",
22 | "author": {
23 | "name": "Ben Plum",
24 | "email": "mr@benplum.com",
25 | "url": "http://www.benplum.com"
26 | },
27 | "devDependencies": {
28 | "grunt-autoprefixer": "^1.0.1",
29 | "grunt-banner": "^0.2.3",
30 | "grunt-contrib-copy": "^0.6.0",
31 | "grunt-contrib-jshint": "~0.7.2",
32 | "grunt-contrib-less": "^0.11.4",
33 | "grunt-contrib-uglify": "~0.2.7",
34 | "grunt-jquerymanifest": "~0.1.3",
35 | "grunt-npm2bower-sync": "~0.3.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/jquery.fs.dropper-config.less:
--------------------------------------------------------------------------------
1 |
2 | // Use these variables when compiling directly
3 |
4 | // Base
5 |
6 | @dropper-background: #fff; // #fff
7 | @dropper-border: 3px dashed #ccc; // 3px dashed #ccc
8 | @dropper-border-radius: 0; // 0
9 |
10 | @dropper-font-size: 14px; // 14px
11 | @dropper-text-color: #666; // #666
12 |
13 | @dropper-margin: 0; // 0
14 | @dropper-padding: 25px; // 25px
15 |
16 | // Dropping
17 |
18 | @dropper-dropping-background: #eee; // #eee
19 | @dropper-dropping-border-color: #999; // #999
20 | @dropper-dropping-text-color: #333; // #333
--------------------------------------------------------------------------------
/src/jquery.fs.dropper-styles.less:
--------------------------------------------------------------------------------
1 |
2 | .dropper {
3 | overflow: hidden;
4 |
5 | &,
6 | & *,
7 | & *:before,
8 | & *:after {
9 | box-sizing: border-box;
10 | }
11 |
12 | // .dropper-dropzone
13 |
14 | &-dropzone {
15 | background: @dropper-background;
16 | border: @dropper-border;
17 | border-radius: @dropper-border-radius;
18 | color: @dropper-text-color;
19 | cursor: pointer;
20 | font-size: @dropper-font-size;
21 | margin: @dropper-margin;
22 | padding: @dropper-padding;
23 | text-align: center;
24 | }
25 |
26 | &.dropping &-dropzone,
27 | .no-touch &:hover &-dropzone {
28 | background: @dropper-dropping-background;
29 | border-color: @dropper-dropping-border-color;
30 | color: @dropper-dropping-text-color;
31 | }
32 |
33 | // .dropper-input
34 |
35 | &-input {
36 | position: absolute;
37 | left: 100%;
38 | opacity: 0;
39 |
40 | // IE8 Opacity Check
41 |
42 | .no-opacity & {
43 | left: -999px;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/jquery.fs.dropper.js:
--------------------------------------------------------------------------------
1 | ;(function ($, window) {
2 | "use strict";
3 |
4 | var supported = (window.File && window.FileReader && window.FileList);
5 |
6 | /**
7 | * @options
8 | * @param action [string] "Where to submit uploads"
9 | * @param label [string] <'Drag and drop files or click to select'> "Dropzone text"
10 | * @param maxQueue [int] <2> "Number of files to simultaneously upload"
11 | * @param maxSize [int] <5242880> "Max file size allowed"
12 | * @param postData [object] "Extra data to post with upload"
13 | * @param postKey [string] <'file'> "Key to upload file as"
14 | */
15 |
16 | var options = {
17 | action: "",
18 | label: "Drag and drop files or click to select",
19 | maxQueue: 2,
20 | maxSize: 5242880, // 5 mb
21 | postData: {},
22 | postKey: "file"
23 | };
24 |
25 | /**
26 | * @events
27 | * @event start.dropper ""
28 | * @event complete.dropper ""
29 | * @event fileStart.dropper ""
30 | * @event fileProgress.dropper ""
31 | * @event fileComplete.dropper ""
32 | * @event fileError.dropper ""
33 | */
34 |
35 | var pub = {
36 |
37 | /**
38 | * @method
39 | * @name defaults
40 | * @description Sets default plugin options
41 | * @param opts [object] <{}> "Options object"
42 | * @example $.dropper("defaults", opts);
43 | */
44 | defaults: function(opts) {
45 | options = $.extend(options, opts || {});
46 |
47 | return (typeof this === 'object') ? $(this) : true;
48 | }
49 | };
50 |
51 | /**
52 | * @method private
53 | * @name _init
54 | * @description Initializes plugin
55 | * @param opts [object] "Initialization options"
56 | */
57 | function _init(opts) {
58 | var $items = $(this);
59 |
60 | if (supported) {
61 | // Settings
62 | opts = $.extend({}, options, opts);
63 |
64 | // Apply to each element
65 | for (var i = 0, count = $items.length; i < count; i++) {
66 | _build($items.eq(i), opts);
67 | }
68 | }
69 |
70 | return $items;
71 | }
72 |
73 | /**
74 | * @method private
75 | * @name _build
76 | * @description Builds each instance
77 | * @param $nav [jQuery object] "Target jQuery object"
78 | * @param opts [object] <{}> "Options object"
79 | */
80 | function _build($dropper, opts) {
81 | opts = $.extend({}, opts, $dropper.data("dropper-options"));
82 |
83 | var html = "";
84 |
85 | html += '';
86 | html += opts.label;
87 | html += '
';
88 | html += ' 1) {
90 | html += ' multiple';
91 | }
92 | html += '>';
93 |
94 | $dropper.addClass("dropper")
95 | .append(html);
96 |
97 | var data = $.extend({
98 | $dropper: $dropper,
99 | $input: $dropper.find(".dropper-input"),
100 | queue: [],
101 | total: 0,
102 | uploading: false
103 | }, opts);
104 |
105 | $dropper.on("click.dropper", ".dropper-dropzone", data, _onClick)
106 | .on("dragenter.dropper", data, _onDragEnter)
107 | .on("dragover.dropper", data, _onDragOver)
108 | .on("dragleave.dropper", data, _onDragOut)
109 | .on("drop.dropper", ".dropper-dropzone", data, _onDrop)
110 | .data("dropper", data);
111 |
112 | data.$input.on("change.dropper", data, _onChange);
113 | }
114 |
115 | /**
116 | * @method private
117 | * @name _onClick
118 | * @description Handles click to dropzone
119 | * @param e [object] "Event data"
120 | */
121 | function _onClick(e) {
122 | e.stopPropagation();
123 | e.preventDefault();
124 |
125 | var data = e.data;
126 |
127 | data.$input.trigger("click");
128 | }
129 |
130 | /**
131 | * @method private
132 | * @name _onChange
133 | * @description Handles change to hidden input
134 | * @param e [object] "Event data"
135 | */
136 | function _onChange(e) {
137 | e.stopPropagation();
138 | e.preventDefault();
139 |
140 | var data = e.data,
141 | files = data.$input[0].files;
142 |
143 | if (files.length) {
144 | _handleUpload(data, files);
145 | }
146 | }
147 |
148 | /**
149 | * @method private
150 | * @name _onDragEnter
151 | * @description Handles dragenter to dropzone
152 | * @param e [object] "Event data"
153 | */
154 | function _onDragEnter(e) {
155 | e.stopPropagation();
156 | e.preventDefault();
157 |
158 | var data = e.data;
159 |
160 | data.$dropper.addClass("dropping");
161 | }
162 |
163 | /**
164 | * @method private
165 | * @name _onDragOver
166 | * @description Handles dragover to dropzone
167 | * @param e [object] "Event data"
168 | */
169 | function _onDragOver(e) {
170 | e.stopPropagation();
171 | e.preventDefault();
172 |
173 | var data = e.data;
174 |
175 | data.$dropper.addClass("dropping");
176 | }
177 |
178 | /**
179 | * @method private
180 | * @name _onDragOut
181 | * @description Handles dragout to dropzone
182 | * @param e [object] "Event data"
183 | */
184 | function _onDragOut(e) {
185 | e.stopPropagation();
186 | e.preventDefault();
187 |
188 | var data = e.data;
189 |
190 | data.$dropper.removeClass("dropping");
191 | }
192 |
193 | /**
194 | * @method private
195 | * @name _onDrop
196 | * @description Handles drop to dropzone
197 | * @param e [object] "Event data"
198 | */
199 | function _onDrop(e) {
200 | e.preventDefault();
201 |
202 | var data = e.data,
203 | files = e.originalEvent.dataTransfer.files;
204 |
205 | data.$dropper.removeClass("dropping");
206 |
207 | _handleUpload(data, files);
208 | }
209 |
210 | /**
211 | * @method private
212 | * @name _handleUpload
213 | * @description Handles new files
214 | * @param data [object] "Instance data"
215 | * @param files [object] "File list"
216 | */
217 | function _handleUpload(data, files) {
218 | var newFiles = [];
219 |
220 | for (var i = 0; i < files.length; i++) {
221 | var file = {
222 | index: data.total++,
223 | file: files[i],
224 | name: files[i].name,
225 | size: files[i].size,
226 | started: false,
227 | complete: false,
228 | error: false,
229 | transfer: null
230 | };
231 |
232 | newFiles.push(file);
233 | data.queue.push(file);
234 | }
235 |
236 | if (!data.uploading) {
237 | $(window).on("beforeunload.dropper", function(){
238 | return 'You have uploads pending, are you sure you want to leave this page?';
239 | });
240 |
241 | data.uploading = true;
242 | }
243 |
244 | data.$dropper.trigger("start.dropper", [ newFiles ]);
245 |
246 | _checkQueue(data);
247 | }
248 |
249 | /**
250 | * @method private
251 | * @name _checkQueue
252 | * @description Checks and updates file queue
253 | * @param data [object] "Instance data"
254 | */
255 | function _checkQueue(data) {
256 | var transfering = 0,
257 | newQueue = [];
258 |
259 | // remove lingering items from queue
260 | for (var i in data.queue) {
261 | if (data.queue.hasOwnProperty(i) && !data.queue[i].complete && !data.queue[i].error) {
262 | newQueue.push(data.queue[i]);
263 | }
264 | }
265 |
266 | data.queue = newQueue;
267 |
268 | for (var j in data.queue) {
269 | if (data.queue.hasOwnProperty(j)) {
270 | if (!data.queue[j].started) {
271 | var formData = new FormData();
272 |
273 | formData.append(data.postKey, data.queue[j].file);
274 |
275 | for (var k in data.postData) {
276 | if (data.postData.hasOwnProperty(k)) {
277 | formData.append(k, data.postData[k]);
278 | }
279 | }
280 |
281 | _uploadFile(data, data.queue[j], formData);
282 | }
283 |
284 | transfering++;
285 |
286 | if (transfering >= data.maxQueue) {
287 | return;
288 | } else {
289 | i++;
290 | }
291 | }
292 | }
293 |
294 | if (transfering === 0) {
295 | $(window).off("beforeunload.dropper");
296 |
297 | data.uploading = false;
298 |
299 | data.$dropper.trigger("complete.dropper");
300 | }
301 | }
302 |
303 | /**
304 | * @method private
305 | * @name _uploadFile
306 | * @description Uploads file
307 | * @param data [object] "Instance data"
308 | * @param file [object] "Target file"
309 | * @param formData [object] "Target form"
310 | */
311 | function _uploadFile(data, file, formData) {
312 | if (file.size >= data.maxSize) {
313 | file.error = true;
314 | data.$dropper.trigger("fileError.dropper", [ file, "Too large" ]);
315 |
316 | _checkQueue(data);
317 | } else {
318 | file.started = true;
319 | file.transfer = $.ajax({
320 | url: data.action,
321 | data: formData,
322 | type: "POST",
323 | contentType:false,
324 | processData: false,
325 | cache: false,
326 | xhr: function() {
327 | var $xhr = $.ajaxSettings.xhr();
328 |
329 | if ($xhr.upload) {
330 | $xhr.upload.addEventListener("progress", function(e) {
331 | var percent = 0,
332 | position = e.loaded || e.position,
333 | total = e.total;
334 |
335 | if (e.lengthComputable) {
336 | percent = Math.ceil(position / total * 100);
337 | }
338 |
339 | data.$dropper.trigger("fileProgress.dropper", [ file, percent ]);
340 | }, false);
341 | }
342 |
343 | return $xhr;
344 | },
345 | beforeSend: function(e) {
346 | data.$dropper.trigger("fileStart.dropper", [ file ]);
347 | },
348 | success: function(response, status, jqXHR) {
349 | file.complete = true;
350 | data.$dropper.trigger("fileComplete.dropper", [ file, response ]);
351 |
352 | _checkQueue(data);
353 | },
354 | error: function(jqXHR, status, error) {
355 | file.error = true;
356 | data.$dropper.trigger("fileError.dropper", [ file, error ]);
357 |
358 | _checkQueue(data);
359 | }
360 | });
361 | }
362 | }
363 |
364 | $.fn.dropper = function(method) {
365 | if (pub[method]) {
366 | return pub[method].apply(this, Array.prototype.slice.call(arguments, 1));
367 | } else if (typeof method === 'object' || !method) {
368 | return _init.apply(this, arguments);
369 | }
370 | return this;
371 | };
372 |
373 | $.dropper = function(method) {
374 | if (method === "defaults") {
375 | pub.defaults.apply(this, Array.prototype.slice.call(arguments, 1));
376 | }
377 | };
378 | })(jQuery, window);
--------------------------------------------------------------------------------
/src/jquery.fs.dropper.less:
--------------------------------------------------------------------------------
1 |
2 | @import "jquery.fs.dropper-config.less";
3 | @import "jquery.fs.dropper-styles.less";
--------------------------------------------------------------------------------