|
1 YUI.add('uploader-html5', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 * This module provides a UI for file selection and multiple file upload capability using |
|
5 * HTML5 XMLHTTPRequest Level 2 as a transport engine. |
|
6 * The supported features include: automatic upload queue management, upload progress |
|
7 * tracking, drag-and-drop support, server response retrieval and error reporting. |
|
8 * |
|
9 * @module uploader-html5 |
|
10 */ |
|
11 |
|
12 // Shorthands for the external modules |
|
13 var substitute = Y.Lang.sub, |
|
14 UploaderQueue = Y.Uploader.Queue; |
|
15 |
|
16 /** |
|
17 * This module provides a UI for file selection and multiple file upload capability using |
|
18 * HTML5 XMLHTTPRequest Level 2 as a transport engine. |
|
19 * @class UploaderHTML5 |
|
20 * @extends Widget |
|
21 * @constructor |
|
22 */ |
|
23 function UploaderHTML5() { |
|
24 UploaderHTML5.superclass.constructor.apply ( this, arguments ); |
|
25 } |
|
26 |
|
27 |
|
28 |
|
29 Y.UploaderHTML5 = Y.extend( UploaderHTML5, Y.Widget, { |
|
30 |
|
31 /** |
|
32 * Stored reference to the instance of the file input field used to |
|
33 * initiate the file selection dialog. |
|
34 * |
|
35 * @property _fileInputField |
|
36 * @type {Node} |
|
37 * @protected |
|
38 */ |
|
39 _fileInputField: null, |
|
40 |
|
41 /** |
|
42 * Stored reference to the click event binding of the `Select Files` |
|
43 * button. |
|
44 * |
|
45 * @property _buttonBinding |
|
46 * @type {EventHandle} |
|
47 * @protected |
|
48 */ |
|
49 _buttonBinding: null, |
|
50 |
|
51 /** |
|
52 * Stored reference to the instance of Uploader.Queue used to manage |
|
53 * the upload process. This is a read-only property that only exists |
|
54 * during an active upload process. Only one queue can be active at |
|
55 * a time; if an upload start is attempted while a queue is active, |
|
56 * it will be ignored. |
|
57 * |
|
58 * @property queue |
|
59 * @type {Uploader.Queue} |
|
60 */ |
|
61 queue: null, |
|
62 |
|
63 // Y.UploaderHTML5 prototype |
|
64 |
|
65 /** |
|
66 * Construction logic executed during UploaderHTML5 instantiation. |
|
67 * |
|
68 * @method initializer |
|
69 * @protected |
|
70 */ |
|
71 initializer : function () { |
|
72 |
|
73 this._fileInputField = null; |
|
74 this.queue = null; |
|
75 this._buttonBinding = null; |
|
76 this._fileList = []; |
|
77 |
|
78 // Publish available events |
|
79 |
|
80 /** |
|
81 * Signals that files have been selected. |
|
82 * |
|
83 * @event fileselect |
|
84 * @param event {Event} The event object for the `fileselect` with the |
|
85 * following payload: |
|
86 * <dl> |
|
87 * <dt>fileList</dt> |
|
88 * <dd>An `Array` of files selected by the user, encapsulated |
|
89 * in Y.FileHTML5 objects.</dd> |
|
90 * </dl> |
|
91 */ |
|
92 this.publish("fileselect"); |
|
93 |
|
94 /** |
|
95 * Signals that an upload of multiple files has been started. |
|
96 * |
|
97 * @event uploadstart |
|
98 * @param event {Event} The event object for the `uploadstart`. |
|
99 */ |
|
100 this.publish("uploadstart"); |
|
101 |
|
102 /** |
|
103 * Signals that an upload of a specific file has started. |
|
104 * |
|
105 * @event fileuploadstart |
|
106 * @param event {Event} The event object for the `fileuploadstart` with the |
|
107 * following payload: |
|
108 * <dl> |
|
109 * <dt>file</dt> |
|
110 * <dd>A reference to the Y.File that dispatched the event.</dd> |
|
111 * <dt>originEvent</dt> |
|
112 * <dd>The original event dispatched by Y.File.</dd> |
|
113 * </dl> |
|
114 */ |
|
115 this.publish("fileuploadstart"); |
|
116 |
|
117 /** |
|
118 * Reports on upload progress of a specific file. |
|
119 * |
|
120 * @event uploadprogress |
|
121 * @param event {Event} The event object for the `uploadprogress` with the |
|
122 * following payload: |
|
123 * <dl> |
|
124 * <dt>file</dt> |
|
125 * <dd>The pointer to the instance of `Y.File` that dispatched the event.</dd> |
|
126 * <dt>bytesLoaded</dt> |
|
127 * <dd>The number of bytes of the file that has been uploaded</dd> |
|
128 * <dt>bytesTotal</dt> |
|
129 * <dd>The total number of bytes in the file</dd> |
|
130 * <dt>percentLoaded</dt> |
|
131 * <dd>The fraction of the file that has been uploaded, out of 100</dd> |
|
132 * <dt>originEvent</dt> |
|
133 * <dd>The original event dispatched by the HTML5 uploader</dd> |
|
134 * </dl> |
|
135 */ |
|
136 this.publish("uploadprogress"); |
|
137 |
|
138 /** |
|
139 * Reports on the total upload progress of the file list. |
|
140 * |
|
141 * @event totaluploadprogress |
|
142 * @param event {Event} The event object for the `totaluploadprogress` with the |
|
143 * following payload: |
|
144 * <dl> |
|
145 * <dt>bytesLoaded</dt> |
|
146 * <dd>The number of bytes of the file list that has been uploaded</dd> |
|
147 * <dt>bytesTotal</dt> |
|
148 * <dd>The total number of bytes in the file list</dd> |
|
149 * <dt>percentLoaded</dt> |
|
150 * <dd>The fraction of the file list that has been uploaded, out of 100</dd> |
|
151 * </dl> |
|
152 */ |
|
153 this.publish("totaluploadprogress"); |
|
154 |
|
155 /** |
|
156 * Signals that a single file upload has been completed. |
|
157 * |
|
158 * @event uploadcomplete |
|
159 * @param event {Event} The event object for the `uploadcomplete` with the |
|
160 * following payload: |
|
161 * <dl> |
|
162 * <dt>file</dt> |
|
163 * <dd>The pointer to the instance of `Y.File` whose upload has been completed.</dd> |
|
164 * <dt>originEvent</dt> |
|
165 * <dd>The original event fired by the SWF Uploader</dd> |
|
166 * <dt>data</dt> |
|
167 * <dd>Data returned by the server.</dd> |
|
168 * </dl> |
|
169 */ |
|
170 this.publish("uploadcomplete"); |
|
171 |
|
172 /** |
|
173 * Signals that the upload process of the entire file list has been completed. |
|
174 * |
|
175 * @event alluploadscomplete |
|
176 * @param event {Event} The event object for the `alluploadscomplete`. |
|
177 */ |
|
178 this.publish("alluploadscomplete"); |
|
179 |
|
180 /** |
|
181 * Signals that a error has occurred in a specific file's upload process. |
|
182 * |
|
183 * @event uploaderror |
|
184 * @param event {Event} The event object for the `uploaderror` with the |
|
185 * following payload: |
|
186 * <dl> |
|
187 * <dt>originEvent</dt> |
|
188 * <dd>The original error event fired by the HTML5 Uploader. </dd> |
|
189 * <dt>file</dt> |
|
190 * <dd>The pointer at the instance of Y.File that returned the error.</dd> |
|
191 * <dt>status</dt> |
|
192 * <dd>The status reported by the XMLHttpRequest object.</dd> |
|
193 * <dt>statusText</dt> |
|
194 * <dd>The statusText reported by the XMLHttpRequest object.</dd> |
|
195 * </dl> |
|
196 */ |
|
197 this.publish("uploaderror"); |
|
198 |
|
199 /** |
|
200 * Signals that a dragged object has entered into the uploader's associated drag-and-drop area. |
|
201 * |
|
202 * @event dragenter |
|
203 * @param event {Event} The event object for the `dragenter`. |
|
204 */ |
|
205 this.publish("dragenter"); |
|
206 |
|
207 /** |
|
208 * Signals that an object has been dragged over the uploader's associated drag-and-drop area. |
|
209 * |
|
210 * @event dragover |
|
211 * @param event {Event} The event object for the `dragover`. |
|
212 */ |
|
213 this.publish("dragover"); |
|
214 |
|
215 /** |
|
216 * Signals that an object has been dragged off of the uploader's associated drag-and-drop area. |
|
217 * |
|
218 * @event dragleave |
|
219 * @param event {Event} The event object for the `dragleave`. |
|
220 */ |
|
221 this.publish("dragleave"); |
|
222 |
|
223 /** |
|
224 * Signals that an object has been dropped over the uploader's associated drag-and-drop area. |
|
225 * |
|
226 * @event drop |
|
227 * @param event {Event} The event object for the `drop` with the |
|
228 * following payload: |
|
229 * <dl> |
|
230 * <dt>fileList</dt> |
|
231 * <dd>An `Array` of files dropped by the user, encapsulated |
|
232 * in Y.FileHTML5 objects.</dd> |
|
233 * </dl> |
|
234 */ |
|
235 this.publish("drop"); |
|
236 |
|
237 }, |
|
238 |
|
239 /** |
|
240 * Create the DOM structure for the UploaderHTML5. |
|
241 * UploaderHTML5's DOM structure consists of a "Select Files" button that can |
|
242 * be replaced by the developer's widget of choice; and a hidden file input field |
|
243 * that is used to instantiate the File Select dialog. |
|
244 * |
|
245 * @method renderUI |
|
246 * @protected |
|
247 */ |
|
248 renderUI : function () { |
|
249 var contentBox = this.get('contentBox'), |
|
250 selButton = this.get("selectFilesButton"); |
|
251 |
|
252 selButton.setStyles({width:"100%", height:"100%"}); |
|
253 contentBox.append(selButton); |
|
254 this._fileInputField = Y.Node.create(UploaderHTML5.HTML5FILEFIELD_TEMPLATE); |
|
255 contentBox.append(this._fileInputField); |
|
256 }, |
|
257 |
|
258 /** |
|
259 * Binds to the UploaderHTML5 UI and subscribes to the necessary events. |
|
260 * |
|
261 * @method bindUI |
|
262 * @protected |
|
263 */ |
|
264 bindUI : function () { |
|
265 |
|
266 this._bindSelectButton(); |
|
267 this._setMultipleFiles(); |
|
268 this._setFileFilters(); |
|
269 this._bindDropArea(); |
|
270 this._triggerEnabled(); |
|
271 |
|
272 this.after("multipleFilesChange", this._setMultipleFiles, this); |
|
273 this.after("fileFiltersChange", this._setFileFilters, this); |
|
274 this.after("enabledChange", this._triggerEnabled, this); |
|
275 this.after("selectFilesButtonChange", this._bindSelectButton, this); |
|
276 this.after("dragAndDropAreaChange", this._bindDropArea, this); |
|
277 this.after("tabIndexChange", function () { |
|
278 this.get("selectFilesButton").set("tabIndex", this.get("tabIndex")); |
|
279 }, this); |
|
280 this._fileInputField.on("change", this._updateFileList, this); |
|
281 this._fileInputField.on("click", function(event) { |
|
282 event.stopPropagation(); |
|
283 }, this); |
|
284 |
|
285 this.get("selectFilesButton").set("tabIndex", this.get("tabIndex")); |
|
286 }, |
|
287 |
|
288 |
|
289 /** |
|
290 * Recreates the file field to null out the previous list of files and |
|
291 * thus allow for an identical file list selection. |
|
292 * |
|
293 * @method _rebindFileField |
|
294 * @protected |
|
295 */ |
|
296 _rebindFileField : function () { |
|
297 this._fileInputField.remove(true); |
|
298 this._fileInputField = Y.Node.create(UploaderHTML5.HTML5FILEFIELD_TEMPLATE); |
|
299 this.get("contentBox").append(this._fileInputField); |
|
300 this._fileInputField.on("change", this._updateFileList, this); |
|
301 this._setMultipleFiles(); |
|
302 this._setFileFilters(); |
|
303 }, |
|
304 |
|
305 |
|
306 /** |
|
307 * Binds the specified drop area's drag and drop events to the |
|
308 * uploader's custom handler. |
|
309 * |
|
310 * @method _bindDropArea |
|
311 * @protected |
|
312 */ |
|
313 _bindDropArea : function (event) { |
|
314 var ev = event || {prevVal: null}, |
|
315 ddArea = this.get("dragAndDropArea"); |
|
316 |
|
317 if (ev.prevVal !== null) { |
|
318 ev.prevVal.detach('drop', this._ddEventHandler); |
|
319 ev.prevVal.detach('dragenter', this._ddEventHandler); |
|
320 ev.prevVal.detach('dragover', this._ddEventHandler); |
|
321 ev.prevVal.detach('dragleave', this._ddEventHandler); |
|
322 } |
|
323 |
|
324 if (ddArea !== null) { |
|
325 ddArea.on('drop', this._ddEventHandler, this); |
|
326 ddArea.on('dragenter', this._ddEventHandler, this); |
|
327 ddArea.on('dragover', this._ddEventHandler, this); |
|
328 ddArea.on('dragleave', this._ddEventHandler, this); |
|
329 } |
|
330 }, |
|
331 |
|
332 /** |
|
333 * Binds the instantiation of the file select dialog to the current file select |
|
334 * control. |
|
335 * |
|
336 * @method _bindSelectButton |
|
337 * @protected |
|
338 */ |
|
339 _bindSelectButton : function () { |
|
340 this._buttonBinding = this.get("selectFilesButton").on("click", this.openFileSelectDialog, this); |
|
341 }, |
|
342 |
|
343 /** |
|
344 * Handles the drag and drop events from the uploader's specified drop |
|
345 * area. |
|
346 * |
|
347 * @method _ddEventHandler |
|
348 * @protected |
|
349 */ |
|
350 _ddEventHandler : function (event) { |
|
351 |
|
352 |
|
353 event.stopPropagation(); |
|
354 event.preventDefault(); |
|
355 |
|
356 if (Y.Array.indexOf(event._event.dataTransfer.types, 'Files') > -1) { |
|
357 switch (event.type) { |
|
358 case "dragenter": |
|
359 this.fire("dragenter"); |
|
360 break; |
|
361 case "dragover": |
|
362 this.fire("dragover"); |
|
363 break; |
|
364 case "dragleave": |
|
365 this.fire("dragleave"); |
|
366 break; |
|
367 case "drop": |
|
368 |
|
369 var newfiles = event._event.dataTransfer.files, |
|
370 parsedFiles = [], |
|
371 filterFunc = this.get("fileFilterFunction"), |
|
372 oldfiles; |
|
373 |
|
374 if (filterFunc) { |
|
375 Y.each(newfiles, function (value) { |
|
376 var newfile = new Y.FileHTML5(value); |
|
377 if (filterFunc(newfile)) { |
|
378 parsedFiles.push(newfile); |
|
379 } |
|
380 }); |
|
381 } |
|
382 else { |
|
383 Y.each(newfiles, function (value) { |
|
384 parsedFiles.push(new Y.FileHTML5(value)); |
|
385 }); |
|
386 } |
|
387 |
|
388 if (parsedFiles.length > 0) { |
|
389 oldfiles = this.get("fileList"); |
|
390 this.set("fileList", |
|
391 this.get("appendNewFiles") ? oldfiles.concat(parsedFiles) : parsedFiles); |
|
392 this.fire("fileselect", {fileList: parsedFiles}); |
|
393 } |
|
394 |
|
395 this.fire("drop", {fileList: parsedFiles}); |
|
396 break; |
|
397 } |
|
398 } |
|
399 }, |
|
400 |
|
401 /** |
|
402 * Adds or removes a specified state CSS class to the underlying uploader button. |
|
403 * |
|
404 * @method _setButtonClass |
|
405 * @protected |
|
406 * @param state {String} The name of the state enumerated in `buttonClassNames` attribute |
|
407 * from which to derive the needed class name. |
|
408 * @param add {Boolean} A Boolean indicating whether to add or remove the class. |
|
409 */ |
|
410 _setButtonClass : function (state, add) { |
|
411 if (add) { |
|
412 this.get("selectFilesButton").addClass(this.get("buttonClassNames")[state]); |
|
413 } |
|
414 else { |
|
415 this.get("selectFilesButton").removeClass(this.get("buttonClassNames")[state]); |
|
416 } |
|
417 }, |
|
418 |
|
419 /** |
|
420 * Syncs the state of the `multipleFiles` attribute between this class |
|
421 * and the file input field. |
|
422 * |
|
423 * @method _setMultipleFiles |
|
424 * @protected |
|
425 */ |
|
426 _setMultipleFiles : function () { |
|
427 if (this.get("multipleFiles") === true) { |
|
428 this._fileInputField.set("multiple", "multiple"); |
|
429 } |
|
430 else { |
|
431 this._fileInputField.set("multiple", ""); |
|
432 } |
|
433 }, |
|
434 |
|
435 /** |
|
436 * Syncs the state of the `fileFilters` attribute between this class |
|
437 * and the file input field. |
|
438 * |
|
439 * @method _setFileFilters |
|
440 * @protected |
|
441 */ |
|
442 _setFileFilters : function () { |
|
443 if (this.get("fileFilters").length > 0) { |
|
444 this._fileInputField.set("accept", this.get("fileFilters").join(",")); |
|
445 } |
|
446 else { |
|
447 this._fileInputField.set("accept", ""); |
|
448 } |
|
449 }, |
|
450 |
|
451 |
|
452 /** |
|
453 * Syncs the state of the `enabled` attribute between this class |
|
454 * and the underlying button. |
|
455 * |
|
456 * @method _triggerEnabled |
|
457 * @private |
|
458 */ |
|
459 _triggerEnabled : function () { |
|
460 if (this.get("enabled") && this._buttonBinding === null) { |
|
461 this._bindSelectButton(); |
|
462 this._setButtonClass("disabled", false); |
|
463 this.get("selectFilesButton").setAttribute("aria-disabled", "false"); |
|
464 } |
|
465 else if (!this.get("enabled") && this._buttonBinding) { |
|
466 this._buttonBinding.detach(); |
|
467 this._buttonBinding = null; |
|
468 this._setButtonClass("disabled", true); |
|
469 this.get("selectFilesButton").setAttribute("aria-disabled", "true"); |
|
470 } |
|
471 }, |
|
472 |
|
473 /** |
|
474 * Getter for the `fileList` attribute |
|
475 * |
|
476 * @method _getFileList |
|
477 * @private |
|
478 */ |
|
479 _getFileList : function () { |
|
480 return this._fileList.concat(); |
|
481 }, |
|
482 |
|
483 /** |
|
484 * Setter for the `fileList` attribute |
|
485 * |
|
486 * @method _setFileList |
|
487 * @private |
|
488 */ |
|
489 _setFileList : function (val) { |
|
490 this._fileList = val.concat(); |
|
491 return this._fileList.concat(); |
|
492 }, |
|
493 |
|
494 /** |
|
495 * Adjusts the content of the `fileList` based on the results of file selection |
|
496 * and the `appendNewFiles` attribute. If the `appendNewFiles` attribute is true, |
|
497 * then selected files are appended to the existing list; otherwise, the list is |
|
498 * cleared and populated with the newly selected files. |
|
499 * |
|
500 * @method _updateFileList |
|
501 * @param ev {Event} The file selection event received from the uploader. |
|
502 * @protected |
|
503 */ |
|
504 _updateFileList : function (ev) { |
|
505 var newfiles = ev.target.getDOMNode().files, |
|
506 parsedFiles = [], |
|
507 filterFunc = this.get("fileFilterFunction"), |
|
508 oldfiles; |
|
509 |
|
510 if (filterFunc) { |
|
511 Y.each(newfiles, function (value) { |
|
512 var newfile = new Y.FileHTML5(value); |
|
513 if (filterFunc(newfile)) { |
|
514 parsedFiles.push(newfile); |
|
515 } |
|
516 }); |
|
517 } |
|
518 else { |
|
519 Y.each(newfiles, function (value) { |
|
520 parsedFiles.push(new Y.FileHTML5(value)); |
|
521 }); |
|
522 } |
|
523 |
|
524 if (parsedFiles.length > 0) { |
|
525 oldfiles = this.get("fileList"); |
|
526 |
|
527 this.set("fileList", |
|
528 this.get("appendNewFiles") ? oldfiles.concat(parsedFiles) : parsedFiles ); |
|
529 |
|
530 this.fire("fileselect", {fileList: parsedFiles}); |
|
531 } |
|
532 |
|
533 this._rebindFileField(); |
|
534 }, |
|
535 |
|
536 |
|
537 /** |
|
538 * Handles and retransmits events fired by `Y.File` and `Y.Uploader.Queue`. |
|
539 * |
|
540 * @method _uploadEventHandler |
|
541 * @param event The event dispatched during the upload process. |
|
542 * @protected |
|
543 */ |
|
544 _uploadEventHandler : function (event) { |
|
545 |
|
546 switch (event.type) { |
|
547 case "file:uploadstart": |
|
548 this.fire("fileuploadstart", event); |
|
549 break; |
|
550 case "file:uploadprogress": |
|
551 this.fire("uploadprogress", event); |
|
552 break; |
|
553 case "uploaderqueue:totaluploadprogress": |
|
554 this.fire("totaluploadprogress", event); |
|
555 break; |
|
556 case "file:uploadcomplete": |
|
557 this.fire("uploadcomplete", event); |
|
558 break; |
|
559 case "uploaderqueue:alluploadscomplete": |
|
560 this.queue = null; |
|
561 this.fire("alluploadscomplete", event); |
|
562 break; |
|
563 case "file:uploaderror": // overflow intentional |
|
564 case "uploaderqueue:uploaderror": |
|
565 this.fire("uploaderror", event); |
|
566 break; |
|
567 case "file:uploadcancel": // overflow intentional |
|
568 case "uploaderqueue:uploadcancel": |
|
569 this.fire("uploadcancel", event); |
|
570 break; |
|
571 } |
|
572 |
|
573 }, |
|
574 |
|
575 /** |
|
576 * Opens the File Selection dialog by simulating a click on the file input field. |
|
577 * |
|
578 * @method openFileSelectDialog |
|
579 */ |
|
580 openFileSelectDialog : function () { |
|
581 var fileDomNode = this._fileInputField.getDOMNode(); |
|
582 if (fileDomNode.click) { |
|
583 fileDomNode.click(); |
|
584 } |
|
585 }, |
|
586 |
|
587 /** |
|
588 * Starts the upload of a specific file. |
|
589 * |
|
590 * @method upload |
|
591 * @param file {File} Reference to the instance of the file to be uploaded. |
|
592 * @param url {String} The URL to upload the file to. |
|
593 * @param postVars {Object} (optional) A set of key-value pairs to send as variables along with the file upload HTTP request. |
|
594 * If not specified, the values from the attribute `postVarsPerFile` are used instead. |
|
595 */ |
|
596 upload : function (file, url, postvars) { |
|
597 |
|
598 var uploadURL = url || this.get("uploadURL"), |
|
599 postVars = postvars || this.get("postVarsPerFile"), |
|
600 fileId = file.get("id"); |
|
601 |
|
602 postVars = postVars.hasOwnProperty(fileId) ? postVars[fileId] : postVars; |
|
603 |
|
604 if (file instanceof Y.FileHTML5) { |
|
605 |
|
606 file.on("uploadstart", this._uploadEventHandler, this); |
|
607 file.on("uploadprogress", this._uploadEventHandler, this); |
|
608 file.on("uploadcomplete", this._uploadEventHandler, this); |
|
609 file.on("uploaderror", this._uploadEventHandler, this); |
|
610 file.on("uploadcancel", this._uploadEventHandler, this); |
|
611 |
|
612 file.startUpload(uploadURL, postVars, this.get("fileFieldName")); |
|
613 } |
|
614 }, |
|
615 |
|
616 /** |
|
617 * Starts the upload of all files on the file list, using an automated queue. |
|
618 * |
|
619 * @method uploadAll |
|
620 * @param url {String} The URL to upload the files to. |
|
621 * @param [postVars] {Object} A set of key-value pairs to send as variables along with the file upload HTTP request. |
|
622 * If not specified, the values from the attribute `postVarsPerFile` are used instead. |
|
623 */ |
|
624 uploadAll : function (url, postvars) { |
|
625 this.uploadThese(this.get("fileList"), url, postvars); |
|
626 }, |
|
627 |
|
628 /** |
|
629 * Starts the upload of the files specified in the first argument, using an automated queue. |
|
630 * |
|
631 * @method uploadThese |
|
632 * @param files {Array} The list of files to upload. |
|
633 * @param url {String} The URL to upload the files to. |
|
634 * @param [postVars] {Object} A set of key-value pairs to send as variables along with the file upload HTTP request. |
|
635 * If not specified, the values from the attribute `postVarsPerFile` are used instead. |
|
636 */ |
|
637 uploadThese : function (files, url, postvars) { |
|
638 if (!this.queue) { |
|
639 var uploadURL = url || this.get("uploadURL"), |
|
640 postVars = postvars || this.get("postVarsPerFile"); |
|
641 |
|
642 this.queue = new UploaderQueue({ |
|
643 simUploads: this.get("simLimit"), |
|
644 errorAction: this.get("errorAction"), |
|
645 fileFieldName: this.get("fileFieldName"), |
|
646 fileList: files, |
|
647 uploadURL: uploadURL, |
|
648 perFileParameters: postVars, |
|
649 retryCount: this.get("retryCount"), |
|
650 uploadHeaders: this.get("uploadHeaders"), |
|
651 withCredentials: this.get("withCredentials") |
|
652 }); |
|
653 |
|
654 this.queue.on("uploadstart", this._uploadEventHandler, this); |
|
655 this.queue.on("uploadprogress", this._uploadEventHandler, this); |
|
656 this.queue.on("totaluploadprogress", this._uploadEventHandler, this); |
|
657 this.queue.on("uploadcomplete", this._uploadEventHandler, this); |
|
658 this.queue.on("alluploadscomplete", this._uploadEventHandler, this); |
|
659 this.queue.on("uploadcancel", this._uploadEventHandler, this); |
|
660 this.queue.on("uploaderror", this._uploadEventHandler, this); |
|
661 this.queue.startUpload(); |
|
662 |
|
663 this.fire("uploadstart"); |
|
664 } |
|
665 else if (this.queue._currentState === UploaderQueue.UPLOADING) { |
|
666 this.queue.set("perFileParameters", this.get("postVarsPerFile")); |
|
667 Y.each(files, function (file) { |
|
668 this.queue.addToQueueBottom(file); |
|
669 }, this); |
|
670 } |
|
671 } |
|
672 }, { |
|
673 |
|
674 /** |
|
675 * The template for the hidden file input field container. The file input field will only |
|
676 * accept clicks if its visibility is set to hidden (and will not if it's `display` value |
|
677 * is set to `none`) |
|
678 * |
|
679 * @property HTML5FILEFIELD_TEMPLATE |
|
680 * @type {String} |
|
681 * @static |
|
682 */ |
|
683 HTML5FILEFIELD_TEMPLATE: "<input type='file' style='visibility:hidden; width:0px; height: 0px;'>", |
|
684 |
|
685 /** |
|
686 * The template for the "Select Files" button. |
|
687 * |
|
688 * @property SELECT_FILES_BUTTON |
|
689 * @type {String} |
|
690 * @static |
|
691 * @default '<button type="button" class="yui3-button" role="button" aria-label="{selectButtonLabel}" |
|
692 * tabindex="{tabIndex}">{selectButtonLabel}</button>' |
|
693 */ |
|
694 SELECT_FILES_BUTTON: '<button type="button" class="yui3-button" role="button" aria-label="{selectButtonLabel}" ' + |
|
695 'tabindex="{tabIndex}">{selectButtonLabel}</button>', |
|
696 |
|
697 /** |
|
698 * The static property reflecting the type of uploader that `Y.Uploader` |
|
699 * aliases. The UploaderHTML5 value is `"html5"`. |
|
700 * |
|
701 * @property TYPE |
|
702 * @type {String} |
|
703 * @static |
|
704 */ |
|
705 TYPE: "html5", |
|
706 |
|
707 /** |
|
708 * The identity of the widget. |
|
709 * |
|
710 * @property NAME |
|
711 * @type String |
|
712 * @default 'uploader' |
|
713 * @readOnly |
|
714 * @protected |
|
715 * @static |
|
716 */ |
|
717 NAME: "uploader", |
|
718 |
|
719 /** |
|
720 * Static property used to define the default attribute configuration of |
|
721 * the Widget. |
|
722 * |
|
723 * @property ATTRS |
|
724 * @type {Object} |
|
725 * @protected |
|
726 * @static |
|
727 */ |
|
728 ATTRS: { |
|
729 |
|
730 /** |
|
731 * A Boolean indicating whether newly selected files should be appended |
|
732 * to the existing file list, or whether they should replace it. |
|
733 * |
|
734 * @attribute appendNewFiles |
|
735 * @type {Boolean} |
|
736 * @default true |
|
737 */ |
|
738 appendNewFiles : { |
|
739 value: true |
|
740 }, |
|
741 |
|
742 /** |
|
743 * The names of CSS classes that correspond to different button states |
|
744 * of the "Select Files" control. These classes are assigned to the |
|
745 * "Select Files" control based on the configuration of the uploader. |
|
746 * Currently, the only class name used is that corresponding to the |
|
747 * `disabled` state of the uploader. Other button states should be managed |
|
748 * directly via CSS selectors. |
|
749 * <ul> |
|
750 * <li> <strong>`disabled`</strong>: the class corresponding to the disabled state |
|
751 * of the "Select Files" button.</li> |
|
752 * </ul> |
|
753 * @attribute buttonClassNames |
|
754 * @type {Object} |
|
755 * @default { |
|
756 * disabled: "yui3-button-disabled" |
|
757 * } |
|
758 */ |
|
759 buttonClassNames: { |
|
760 value: { |
|
761 "hover": "yui3-button-hover", |
|
762 "active": "yui3-button-active", |
|
763 "disabled": "yui3-button-disabled", |
|
764 "focus": "yui3-button-selected" |
|
765 } |
|
766 }, |
|
767 |
|
768 /** |
|
769 * The node that serves as the drop target for files. |
|
770 * |
|
771 * @attribute dragAndDropArea |
|
772 * @type {Node} |
|
773 * @default null |
|
774 */ |
|
775 dragAndDropArea: { |
|
776 value: null, |
|
777 setter: function (val) { |
|
778 return Y.one(val); |
|
779 } |
|
780 }, |
|
781 |
|
782 /** |
|
783 * A Boolean indicating whether the uploader is enabled or disabled for user input. |
|
784 * |
|
785 * @attribute enabled |
|
786 * @type {Boolean} |
|
787 * @default true |
|
788 */ |
|
789 enabled : { |
|
790 value: true |
|
791 }, |
|
792 |
|
793 /** |
|
794 * The action performed when an upload error occurs for a specific file being uploaded. |
|
795 * The possible values are: |
|
796 * <ul> |
|
797 * <li> <strong>`UploaderQueue.CONTINUE`</strong>: the error is ignored and the upload process is continued.</li> |
|
798 * <li> <strong>`UploaderQueue.STOP`</strong>: the upload process is stopped as soon as any other parallel file |
|
799 * uploads are finished.</li> |
|
800 * <li> <strong>`UploaderQueue.RESTART_ASAP`</strong>: the file is added back to the front of the queue.</li> |
|
801 * <li> <strong>`UploaderQueue.RESTART_AFTER`</strong>: the file is added to the back of the queue.</li> |
|
802 * </ul> |
|
803 * @attribute errorAction |
|
804 * @type {String} |
|
805 * @default UploaderQueue.CONTINUE |
|
806 */ |
|
807 errorAction: { |
|
808 value: "continue", |
|
809 validator: function (val) { |
|
810 return ( |
|
811 val === UploaderQueue.CONTINUE || |
|
812 val === UploaderQueue.STOP || |
|
813 val === UploaderQueue.RESTART_ASAP || |
|
814 val === UploaderQueue.RESTART_AFTER |
|
815 ); |
|
816 } |
|
817 }, |
|
818 |
|
819 /** |
|
820 * An array indicating what fileFilters should be applied to the file |
|
821 * selection dialog. Each element in the array should be a string |
|
822 * indicating the Media (MIME) type for the files that should be supported |
|
823 * for selection. The Media type strings should be properly formatted |
|
824 * or this parameter will be ignored. Examples of valid strings include: |
|
825 * "audio/*", "video/*", "application/pdf", etc. More information |
|
826 * on valid Media type strings is available here: |
|
827 * http://www.iana.org/assignments/media-types/index.html |
|
828 * @attribute fileFilters |
|
829 * @type {Array} |
|
830 * @default [] |
|
831 */ |
|
832 fileFilters: { |
|
833 value: [] |
|
834 }, |
|
835 |
|
836 /** |
|
837 * A filtering function that is applied to every file selected by the user. |
|
838 * The function receives the `Y.File` object and must return a Boolean value. |
|
839 * If a `false` value is returned, the file in question is not added to the |
|
840 * list of files to be uploaded. |
|
841 * Use this function to put limits on file sizes or check the file names for |
|
842 * correct extension, but make sure that a server-side check is also performed, |
|
843 * since any client-side restrictions are only advisory and can be circumvented. |
|
844 * |
|
845 * @attribute fileFilterFunction |
|
846 * @type {Function} |
|
847 * @default null |
|
848 */ |
|
849 fileFilterFunction: { |
|
850 value: null |
|
851 }, |
|
852 |
|
853 /** |
|
854 * A String specifying what should be the POST field name for the file |
|
855 * content in the upload request. |
|
856 * |
|
857 * @attribute fileFieldName |
|
858 * @type {String} |
|
859 * @default Filedata |
|
860 */ |
|
861 fileFieldName: { |
|
862 value: "Filedata" |
|
863 }, |
|
864 |
|
865 /** |
|
866 * The array of files to be uploaded. All elements in the array |
|
867 * must be instances of `Y.File` and be instantiated with an instance |
|
868 * of native JavaScript File() class. |
|
869 * |
|
870 * @attribute fileList |
|
871 * @type {Array} |
|
872 * @default [] |
|
873 */ |
|
874 fileList: { |
|
875 value: [], |
|
876 getter: "_getFileList", |
|
877 setter: "_setFileList" |
|
878 }, |
|
879 |
|
880 /** |
|
881 * A Boolean indicating whether multiple file selection is enabled. |
|
882 * |
|
883 * @attribute multipleFiles |
|
884 * @type {Boolean} |
|
885 * @default false |
|
886 */ |
|
887 multipleFiles: { |
|
888 value: false |
|
889 }, |
|
890 |
|
891 /** |
|
892 * An object, keyed by `fileId`, containing sets of key-value pairs |
|
893 * that should be passed as POST variables along with each corresponding |
|
894 * file. This attribute is only used if no POST variables are specifed |
|
895 * in the upload method call. |
|
896 * |
|
897 * @attribute postVarsPerFile |
|
898 * @type {Object} |
|
899 * @default {} |
|
900 */ |
|
901 postVarsPerFile: { |
|
902 value: {} |
|
903 }, |
|
904 |
|
905 /** |
|
906 * The label for the "Select Files" widget. This is the value that replaces the |
|
907 * `{selectButtonLabel}` token in the `SELECT_FILES_BUTTON` template. |
|
908 * |
|
909 * @attribute selectButtonLabel |
|
910 * @type {String} |
|
911 * @default "Select Files" |
|
912 */ |
|
913 selectButtonLabel: { |
|
914 value: "Select Files" |
|
915 }, |
|
916 |
|
917 /** |
|
918 * The widget that serves as the "Select Files control for the file uploader |
|
919 * |
|
920 * |
|
921 * @attribute selectFilesButton |
|
922 * @type {Node | Widget} |
|
923 * @default A standard HTML button with YUI CSS Button skin. |
|
924 */ |
|
925 selectFilesButton : { |
|
926 valueFn: function () { |
|
927 return Y.Node.create(substitute(Y.UploaderHTML5.SELECT_FILES_BUTTON, { |
|
928 selectButtonLabel: this.get("selectButtonLabel"), |
|
929 tabIndex: this.get("tabIndex") |
|
930 })); |
|
931 } |
|
932 }, |
|
933 |
|
934 /** |
|
935 * The number of files that can be uploaded |
|
936 * simultaneously if the automatic queue management |
|
937 * is used. This value can be in the range between 2 |
|
938 * and 5. |
|
939 * |
|
940 * @attribute simLimit |
|
941 * @type {Number} |
|
942 * @default 2 |
|
943 */ |
|
944 simLimit: { |
|
945 value: 2, |
|
946 validator: function (val) { |
|
947 return (val >= 1 && val <= 5); |
|
948 } |
|
949 }, |
|
950 |
|
951 /** |
|
952 * The URL to which file upload requested are POSTed. Only used if a different url is not passed to the upload method call. |
|
953 * |
|
954 * @attribute uploadURL |
|
955 * @type {String} |
|
956 * @default "" |
|
957 */ |
|
958 uploadURL: { |
|
959 value: "" |
|
960 }, |
|
961 |
|
962 /** |
|
963 * Additional HTTP headers that should be included |
|
964 * in the upload request. |
|
965 * |
|
966 * |
|
967 * @attribute uploadHeaders |
|
968 * @type {Object} |
|
969 * @default {} |
|
970 */ |
|
971 uploadHeaders: { |
|
972 value: {} |
|
973 }, |
|
974 |
|
975 /** |
|
976 * A Boolean that specifies whether the file should be |
|
977 * uploaded with the appropriate user credentials for the |
|
978 * domain. |
|
979 * |
|
980 * @attribute withCredentials |
|
981 * @type {Boolean} |
|
982 * @default true |
|
983 */ |
|
984 withCredentials: { |
|
985 value: true |
|
986 }, |
|
987 |
|
988 /** |
|
989 * The number of times to try re-uploading a file that failed to upload before |
|
990 * cancelling its upload. |
|
991 * |
|
992 * @attribute retryCount |
|
993 * @type {Number} |
|
994 * @default 3 |
|
995 */ |
|
996 retryCount: { |
|
997 value: 3 |
|
998 } |
|
999 } |
|
1000 }); |
|
1001 |
|
1002 Y.UploaderHTML5.Queue = UploaderQueue; |
|
1003 |
|
1004 |
|
1005 |
|
1006 }, '@VERSION@', {"requires": ["widget", "node-event-simulate", "file-html5", "uploader-queue"]}); |