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