001/* 002 * #%L 003 * GwtMaterial 004 * %% 005 * Copyright (C) 2015 - 2017 GwtMaterialDesign 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package gwt.material.design.addins.client.fileuploader; 021 022import com.google.gwt.core.client.GWT; 023import com.google.gwt.dom.client.Document; 024import com.google.gwt.dom.client.Element; 025import com.google.gwt.event.shared.HandlerRegistration; 026import com.google.gwt.user.client.DOM; 027import gwt.material.design.addins.client.MaterialAddins; 028import gwt.material.design.addins.client.base.constants.AddinsCssName; 029import gwt.material.design.addins.client.fileuploader.base.HasFileUpload; 030import gwt.material.design.addins.client.fileuploader.base.UploadFile; 031import gwt.material.design.addins.client.fileuploader.base.UploadResponse; 032import gwt.material.design.addins.client.fileuploader.constants.FileMethod; 033import gwt.material.design.addins.client.fileuploader.constants.FileUploaderEvents; 034import gwt.material.design.addins.client.fileuploader.events.*; 035import gwt.material.design.addins.client.fileuploader.js.Dropzone; 036import gwt.material.design.addins.client.fileuploader.js.File; 037import gwt.material.design.addins.client.fileuploader.js.JsFileUploaderOptions; 038import gwt.material.design.client.MaterialDesignBase; 039import gwt.material.design.client.base.JsLoader; 040import gwt.material.design.client.base.MaterialWidget; 041import gwt.material.design.client.constants.CssName; 042import gwt.material.design.client.constants.Display; 043import gwt.material.design.client.events.*; 044import gwt.material.design.client.ui.MaterialToast; 045import gwt.material.design.jquery.client.api.JQueryElement; 046 047import java.util.Date; 048 049import static gwt.material.design.jquery.client.api.JQuery.$; 050 051//@formatter:off 052 053/** 054 * Custom file uploader with Dnd support with the help of dropzone.js. It has multiple 055 * feature just like the GWT File Uploader core widget. 056 * <p> 057 * <h3>XML Namespace Declaration</h3> 058 * <pre> 059 * {@code 060 * xmlns:ma='urn:import:gwt.material.design.addins.client' 061 * } 062 * </pre> 063 * <p> 064 * <h3>UiBinder Usage:</h3> 065 * <pre> 066 * {@code 067 * <ma:fileuploader.MaterialFileUploader url="/file/upload"/> 068 * } 069 * </pre> 070 * 071 * @author kevzlou7979 072 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#fileuploader">File Uploader</a> 073 * @see <a href="https://github.com/enyo/dropzone">Dropzone 4.3.0</a> 074 */ 075//@formatter:on 076public class MaterialFileUploader extends MaterialWidget implements JsLoader, HasFileUpload<UploadFile> { 077 078 static { 079 if (MaterialAddins.isDebug()) { 080 MaterialDesignBase.injectDebugJs(MaterialFileUploaderDebugClientBundle.INSTANCE.dropzoneJsDebug()); 081 MaterialDesignBase.injectCss(MaterialFileUploaderDebugClientBundle.INSTANCE.dropzoneCssDebug()); 082 } else { 083 MaterialDesignBase.injectJs(MaterialFileUploaderClientBundle.INSTANCE.dropzoneJs()); 084 MaterialDesignBase.injectCss(MaterialFileUploaderClientBundle.INSTANCE.dropzoneCss()); 085 } 086 } 087 088 private boolean preview = true; 089 private boolean enabled = true; 090 private int totalFiles = 0; 091 private String globalResponse = ""; 092 private Dropzone uploader; 093 private MaterialUploadPreview uploadPreview = new MaterialUploadPreview(); 094 private JsFileUploaderOptions options = new JsFileUploaderOptions(); 095 096 public MaterialFileUploader() { 097 super(Document.get().createDivElement(), AddinsCssName.FILEUPLOADER); 098 099 setId(AddinsCssName.ZDROP); 100 add(uploadPreview); 101 } 102 103 public MaterialFileUploader(String url, FileMethod method) { 104 this(); 105 setUrl(url); 106 setMethod(method); 107 } 108 109 public MaterialFileUploader(String url, FileMethod method, int maxFileSize, String acceptedFiles) { 110 this(url, method); 111 setMaxFiles(maxFileSize); 112 setAcceptedFiles(acceptedFiles); 113 } 114 115 @Override 116 protected void onLoad() { 117 super.onLoad(); 118 119 if (getWidgetCount() > 1) { 120 uploadPreview.getUploadCollection().setId(DOM.createUniqueId()); 121 if (options.clickable == null) { 122 String clickable = DOM.createUniqueId(); 123 options.clickable = "#" + clickable; 124 if (getWidget(1) instanceof MaterialUploadLabel) { 125 MaterialUploadLabel label = (MaterialUploadLabel) getWidget(1); 126 label.getIcon().setId(clickable); 127 } else { 128 getWidget(1).getElement().setId(clickable); 129 } 130 131 } 132 133 if (!isPreview()) { 134 uploadPreview.setDisplay(Display.NONE); 135 } 136 137 load(); 138 139 } else { 140 GWT.log("You don't have any child widget to use as a upload label"); 141 } 142 143 setEnabled(enabled); 144 } 145 146 @Override 147 public void load() { 148 MaterialUploadCollection uploadCollection = uploadPreview.getUploadCollection(); 149 if (uploadCollection != null) { 150 initDropzone(getElement(), 151 uploadCollection.getItem().getElement(), 152 uploadCollection.getId(), 153 uploadCollection.getElement(), 154 uploadPreview.getUploadHeader().getUploadedFiles().getElement()); 155 } 156 } 157 158 @Override 159 protected void onUnload() { 160 super.onUnload(); 161 unload(); 162 } 163 164 @Override 165 public void unload() { 166 if (uploader != null) { 167 uploader.destroy(); 168 } 169 } 170 171 @Override 172 public void reload() { 173 unload(); 174 load(); 175 } 176 177 178 /** 179 * Intialize the dropzone component with element and form url to provide a 180 * dnd feature for the file upload 181 * 182 * @param e 183 */ 184 protected void initDropzone(Element e, Element template, String previews, Element uploadPreview, Element uploadedFiles) { 185 JQueryElement previewNode = $(template); 186 previewNode.asElement().setId(""); 187 String previewTemplate = previewNode.parent().html(); 188 options.previewTemplate = previewTemplate; 189 options.previewsContainer = "#" + previews; 190 uploader = new Dropzone(e, options); 191 192 uploader.on(FileUploaderEvents.DROP, event -> { 193 fireDropEvent(); 194 if (preview) { 195 $(e).removeClass(CssName.ACTIVE); 196 } 197 return true; 198 }); 199 200 uploader.on(FileUploaderEvents.DRAG_START, event -> { 201 DragStartEvent.fire(this); 202 return true; 203 }); 204 205 uploader.on(FileUploaderEvents.DRAG_END, event -> { 206 DragEndEvent.fire(this); 207 return true; 208 }); 209 210 uploader.on(FileUploaderEvents.DRAG_ENTER, event -> { 211 DragEnterEvent.fire(this, null); 212 if (preview) { 213 $(e).addClass(CssName.ACTIVE); 214 } 215 return true; 216 }); 217 218 uploader.on(FileUploaderEvents.DRAG_OVER, event -> { 219 DragOverEvent.fire(this); 220 return true; 221 }); 222 223 uploader.on(FileUploaderEvents.DRAG_LEAVE, event -> { 224 DragLeaveEvent.fire(this, null); 225 if (preview) { 226 $(e).removeClass(CssName.ACTIVE); 227 } 228 return true; 229 }); 230 231 uploader.on(FileUploaderEvents.ADDED_FILE, file -> { 232 AddedFileEvent.fire(this, convertUploadFile(file)); 233 totalFiles++; 234 235 if (isPreview()) { 236 $(uploadPreview).css("visibility", "visible"); 237 $(uploadedFiles).html("Uploaded files " + totalFiles); 238 getUploadPreview().getUploadHeader().getProgress().setPercent(0); 239 } 240 }); 241 242 uploader.on(FileUploaderEvents.REMOVED_FILE, file -> { 243 RemovedFileEvent.fire(this, convertUploadFile(file)); 244 totalFiles -= 1; 245 $(uploadedFiles).html("Uploaded files " + totalFiles); 246 }); 247 248 uploader.on("error", (file, response) -> { 249 String code = "200"; 250 if (file.xhr != null) { 251 code = file.xhr.status; 252 } 253 254 if (response.indexOf("401") >= 0) { 255 response = "Unauthorized. Your session may have expired. Log in and try again."; 256 globalResponse = response; 257 UnauthorizedEvent.fire(this, convertUploadFile(file), new UploadResponse(file.xhr.status, file.xhr.statusText, response)); 258 } 259 260 if (response.indexOf("404") >= 0) { 261 response = "There is a problem uploading your file."; 262 globalResponse = response; 263 } 264 265 if (response.indexOf("500") >= 0) { 266 response = "There is a problem uploading your file."; 267 globalResponse = response; 268 } 269 270 $(file.previewElement).find("#error-message").html(response); 271 ErrorEvent.fire(this, convertUploadFile(file), new UploadResponse(file.xhr.status, file.xhr.statusText, response)); 272 }); 273 274 uploader.on(FileUploaderEvents.TOTAL_UPLOAD_PROGRESS, (progress, file, response) -> { 275 TotalUploadProgressEvent.fire(this, progress); 276 if (isPreview()) { 277 getUploadPreview().getUploadHeader().getProgress().setPercent(progress); 278 } 279 }); 280 281 uploader.on(FileUploaderEvents.UPLOAD_PROGRESS, (progress, file, response) -> { 282 CurrentUploadProgressEvent.fire(this, progress); 283 if ($this != null) { 284 $this.find(".progress .determinate").css("width", progress + "%"); 285 } 286 }); 287 288 uploader.on(FileUploaderEvents.SENDING, file -> { 289 SendingEvent.fire(this, convertUploadFile(file), new UploadResponse(file.xhr.status, file.xhr.statusText)); 290 }); 291 292 uploader.on(FileUploaderEvents.SUCCESS, (file, response) -> { 293 globalResponse = response; 294 SuccessEvent.fire(this, convertUploadFile(file), new UploadResponse(file.xhr.status, file.xhr.statusText, response)); 295 }); 296 297 uploader.on(FileUploaderEvents.COMPLETE, file -> { 298 CompleteEvent.fire(this, convertUploadFile(file), new UploadResponse(file.xhr.status, file.xhr.statusText, globalResponse)); 299 }); 300 301 uploader.on(FileUploaderEvents.CANCELED, file -> { 302 CanceledEvent.fire(this, convertUploadFile(file)); 303 }); 304 305 uploader.on(FileUploaderEvents.MAX_FILES_REACHED, file -> { 306 MaxFilesReachedEvent.fire(this, convertUploadFile(file)); 307 }); 308 309 uploader.on(FileUploaderEvents.MAX_FILES_EXCEEDED, file -> { 310 MaterialToast.fireToast("You have reached the maximum files to be uploaded."); 311 MaxFilesExceededEvent.fire(this, convertUploadFile(file)); 312 }); 313 } 314 315 316 @Override 317 public void setEnabled(boolean enabled) { 318 this.enabled = enabled; 319 320 if (enabled) { 321 if (uploader != null) uploader.setupEventListeners(); 322 removeStyleName(CssName.DISABLED); 323 } else { 324 if (uploader != null) uploader.removeEventListeners(); 325 addStyleName(CssName.DISABLED); 326 } 327 328 getEnabledMixin().updateWaves(enabled, this); 329 } 330 331 @Override 332 public boolean isEnabled() { 333 return !getElement().hasClassName(CssName.DISABLED); 334 } 335 336 /** 337 * Converts a Native File Object to Upload File object 338 */ 339 protected UploadFile convertUploadFile(File file) { 340 Date lastModifiedDate = new Date(); 341 // Avoid parsing error on last modified date 342 if (file.lastModifiedDate != null && !file.lastModifiedDate.isEmpty()) { 343 lastModifiedDate = new Date(file.lastModifiedDate); 344 } 345 return new UploadFile(file.name, lastModifiedDate, Double.parseDouble(file.size), file.type); 346 } 347 348 /** 349 * Manually start upload queued files when option autoProcessQueue is disabled 350 */ 351 public void processQueue() { 352 uploader.processQueue(); 353 } 354 355 /** 356 * Manually enqueue file when option autoQueue is disabled 357 */ 358 public void enqueueFile(File file) { 359 uploader.enqueueFile(file); 360 } 361 362 /** 363 * Get the form url. 364 */ 365 public String getUrl() { 366 return options.url; 367 } 368 369 /** 370 * Set the form url e.g /file/post. 371 */ 372 public void setUrl(String url) { 373 options.url = url; 374 } 375 376 /** 377 * Get the maximum file size value of the uploader. 378 */ 379 public int getMaxFileSize() { 380 return options.maxFilesize; 381 } 382 383 /** 384 * Set the maximum file size of the uploader, default 20(MB). 385 */ 386 public void setMaxFileSize(int maxFileSize) { 387 options.maxFilesize = maxFileSize; 388 } 389 390 /** 391 * Check whether it's auto process queue or not. 392 */ 393 public boolean isAutoProcessQueue() { 394 return options.autoProcessQueue; 395 } 396 397 /** 398 * Set the auto process queue boolean value. 399 */ 400 public void setAutoProcessQueue(boolean autoProcessQueue) { 401 options.autoProcessQueue = autoProcessQueue; 402 } 403 404 /** 405 * Check whether it's auto queue or not. 406 */ 407 public boolean isAutoQueue() { 408 return options.autoQueue; 409 } 410 411 /** 412 * Set the auto queue boolean value. 413 */ 414 public void setAutoQueue(boolean autoQueue) { 415 options.autoQueue = autoQueue; 416 } 417 418 /** 419 * Get the method param of file uploader. 420 */ 421 public FileMethod getMethod() { 422 return FileMethod.fromStyleName(options.method); 423 } 424 425 /** 426 * Set the method param of file upload (POST or PUT), default POST. 427 */ 428 public void setMethod(FileMethod method) { 429 options.method = method.getCssName(); 430 } 431 432 /** 433 * Get the max number of files. 434 */ 435 public int getMaxFiles() { 436 return options.maxFiles; 437 } 438 439 /** 440 * Set the max number of files. 441 * Default 100 but if you want to accept only one file just set the max file to 1. 442 * If the number of files you upload exceeds, the event maxfilesexceeded will be called. 443 */ 444 public void setMaxFiles(int maxFiles) { 445 options.maxFiles = maxFiles; 446 } 447 448 /** 449 * Check whether it's withCredentials or not. 450 */ 451 public boolean isWithCredentials() { 452 return options.withCredentials; 453 } 454 455 /** 456 * Set the withCredentials boolean value. 457 */ 458 public void setWithCredentials(boolean withCredentials) { 459 options.withCredentials = withCredentials; 460 } 461 462 /** 463 * Get the accepted file string. 464 */ 465 public String getAcceptedFiles() { 466 return options.acceptedFiles; 467 } 468 469 /** 470 * Set the default implementation of accept checks the file's mime type or extension against this list. 471 * This is a comma separated list of mime types or file extensions. Eg.: image/*,application/pdf,.psd. 472 */ 473 public void setAcceptedFiles(String acceptedFiles) { 474 options.acceptedFiles = acceptedFiles; 475 } 476 477 public void fireDropEvent() { 478 DropEvent.fire(this, null); 479 } 480 481 public String getClickable() { 482 return options.clickable.length() == 0 ? options.clickable : options.clickable.substring(1); 483 } 484 485 public void setClickable(String clickable) { 486 options.clickable = "#" + clickable; 487 } 488 489 public boolean isPreview() { 490 return preview; 491 } 492 493 public void setPreview(boolean preview) { 494 this.preview = preview; 495 } 496 497 public void reset() { 498 uploader.removeAllFiles(); 499 } 500 501 public MaterialUploadPreview getUploadPreview() { 502 return uploadPreview; 503 } 504 505 public String getDictDefaultMessage() { 506 return options.dictDefaultMessage; 507 } 508 509 /** 510 * Defaults to "Drop files here to upload" 511 */ 512 public void setDictDefaultMessage(String dictDefaultMessage) { 513 options.dictDefaultMessage = dictDefaultMessage; 514 } 515 516 public String getDictFallbackMessage() { 517 return options.dictFallbackMessage; 518 } 519 520 /** 521 * Defaults to "Your browser does not support drag'n'drop file uploads."\ 522 */ 523 public void setDictFallbackMessage(String dictFallbackMessage) { 524 options.dictFallbackMessage = dictFallbackMessage; 525 } 526 527 public String getDictFallbackText() { 528 return options.dictFallbackText; 529 } 530 531 /** 532 * Defaults to "Please use the fallback form below to upload your files like in the olden days." 533 */ 534 public void setDictFallbackText(String dictFallbackText) { 535 options.dictFallbackText = dictFallbackText; 536 } 537 538 public String getDictFileTooBig() { 539 return options.dictFileTooBig; 540 } 541 542 /** 543 * Defaults to "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB." 544 */ 545 public void setDictFileTooBig(String dictFileTooBig) { 546 options.dictFileTooBig = dictFileTooBig; 547 } 548 549 public String getDictInvalidFileType() { 550 return options.dictInvalidFileType; 551 } 552 553 /** 554 * Defaults to "You can't upload files of this type." 555 */ 556 public void setDictInvalidFileType(String dictInvalidFileType) { 557 options.dictInvalidFileType = dictInvalidFileType; 558 } 559 560 public String getDictResponseError() { 561 return options.dictResponseError; 562 } 563 564 /** 565 * Defaults to "Server responded with {{statusCode}} code." 566 */ 567 public void setDictResponseError(String dictResponseError) { 568 options.dictResponseError = dictResponseError; 569 } 570 571 public String getDictCancelUpload() { 572 return options.dictCancelUpload; 573 } 574 575 /** 576 * Defaults to "Cancel upload" 577 */ 578 public void setDictCancelUpload(String dictCancelUpload) { 579 options.dictCancelUpload = dictCancelUpload; 580 } 581 582 public String getDictCancelUploadConfirmation() { 583 return options.dictCancelUploadConfirmation; 584 } 585 586 /** 587 * Defaults to "Are you sure you want to cancel this upload?" 588 */ 589 public void setDictCancelUploadConfirmation(String dictCancelUploadConfirmation) { 590 options.dictCancelUploadConfirmation = dictCancelUploadConfirmation; 591 } 592 593 public String getDictRemoveFile() { 594 return options.dictRemoveFile; 595 } 596 597 /** 598 * Defaults to "Remove file" 599 */ 600 public void setDictRemoveFile(String dictRemoveFile) { 601 options.dictRemoveFile = dictRemoveFile; 602 } 603 604 public String getDictMaxFilesExceeded() { 605 return options.dictMaxFilesExceeded; 606 } 607 608 /** 609 * Defaults to "You can not upload any more files." 610 */ 611 public void setDictMaxFilesExceeded(String dictMaxFilesExceeded) { 612 options.dictMaxFilesExceeded = dictMaxFilesExceeded; 613 } 614 615 @Override 616 public HandlerRegistration addAddedFileHandler(final AddedFileEvent.AddedFileHandler<UploadFile> handler) { 617 return addHandler(new AddedFileEvent.AddedFileHandler<UploadFile>() { 618 @Override 619 public void onAddedFile(AddedFileEvent<UploadFile> event) { 620 if (isEnabled()) { 621 handler.onAddedFile(event); 622 } 623 } 624 }, AddedFileEvent.getType()); 625 } 626 627 @Override 628 public HandlerRegistration addRemovedFileHandler(final RemovedFileEvent.RemovedFileHandler<UploadFile> handler) { 629 return addHandler(new RemovedFileEvent.RemovedFileHandler<UploadFile>() { 630 @Override 631 public void onRemovedFile(RemovedFileEvent<UploadFile> event) { 632 if (isEnabled()) { 633 handler.onRemovedFile(event); 634 } 635 } 636 }, RemovedFileEvent.getType()); 637 } 638 639 @Override 640 public HandlerRegistration addErrorHandler(final ErrorEvent.ErrorHandler<UploadFile> handler) { 641 return addHandler(new ErrorEvent.ErrorHandler<UploadFile>() { 642 @Override 643 public void onError(ErrorEvent<UploadFile> event) { 644 if (isEnabled()) { 645 handler.onError(event); 646 } 647 } 648 }, ErrorEvent.getType()); 649 } 650 651 @Override 652 public HandlerRegistration addUnauthorizedHandler(final UnauthorizedEvent.UnauthorizedHandler<UploadFile> handler) { 653 return addHandler(new UnauthorizedEvent.UnauthorizedHandler<UploadFile>() { 654 @Override 655 public void onUnauthorized(UnauthorizedEvent<UploadFile> event) { 656 if (isEnabled()) { 657 handler.onUnauthorized(event); 658 } 659 } 660 }, UnauthorizedEvent.getType()); 661 } 662 663 @Override 664 public HandlerRegistration addTotalUploadProgressHandler(final TotalUploadProgressEvent.TotalUploadProgressHandler handler) { 665 return addHandler(event -> { 666 if (isEnabled()) { 667 handler.onTotalUploadProgress(event); 668 } 669 }, TotalUploadProgressEvent.TYPE); 670 } 671 672 @Override 673 public HandlerRegistration addCurrentUploadProgressHandler(CurrentUploadProgressEvent.CurrentUploadProgressHandler handler) { 674 return addHandler(event -> { 675 if (isEnabled()) { 676 handler.onCurrentUploadProgress(event); 677 } 678 }, CurrentUploadProgressEvent.TYPE); 679 } 680 681 @Override 682 public HandlerRegistration addSendingHandler(final SendingEvent.SendingHandler<UploadFile> handler) { 683 return addHandler(new SendingEvent.SendingHandler<UploadFile>() { 684 @Override 685 public void onSending(SendingEvent<UploadFile> event) { 686 if (isEnabled()) { 687 handler.onSending(event); 688 } 689 } 690 }, SendingEvent.getType()); 691 } 692 693 @Override 694 public HandlerRegistration addSuccessHandler(final SuccessEvent.SuccessHandler<UploadFile> handler) { 695 return addHandler(new SuccessEvent.SuccessHandler<UploadFile>() { 696 @Override 697 public void onSuccess(SuccessEvent<UploadFile> event) { 698 if (isEnabled()) { 699 handler.onSuccess(event); 700 } 701 } 702 }, SuccessEvent.getType()); 703 } 704 705 @Override 706 public HandlerRegistration addCompleteHandler(final CompleteEvent.CompleteHandler<UploadFile> handler) { 707 return addHandler(new CompleteEvent.CompleteHandler<UploadFile>() { 708 @Override 709 public void onComplete(CompleteEvent<UploadFile> event) { 710 if (isEnabled()) { 711 handler.onComplete(event); 712 } 713 } 714 }, CompleteEvent.getType()); 715 } 716 717 @Override 718 public HandlerRegistration addCancelHandler(final CanceledEvent.CanceledHandler<UploadFile> handler) { 719 return addHandler(new CanceledEvent.CanceledHandler<UploadFile>() { 720 @Override 721 public void onCanceled(CanceledEvent<UploadFile> event) { 722 if (isEnabled()) { 723 handler.onCanceled(event); 724 } 725 } 726 }, CanceledEvent.getType()); 727 } 728 729 @Override 730 public HandlerRegistration addMaxFilesReachHandler(final MaxFilesReachedEvent.MaxFilesReachedHandler<UploadFile> handler) { 731 return addHandler(new MaxFilesReachedEvent.MaxFilesReachedHandler<UploadFile>() { 732 @Override 733 public void onMaxFilesReached(MaxFilesReachedEvent<UploadFile> event) { 734 if (isEnabled()) { 735 handler.onMaxFilesReached(event); 736 } 737 } 738 }, MaxFilesReachedEvent.getType()); 739 } 740 741 @Override 742 public HandlerRegistration addMaxFilesExceededHandler(final MaxFilesExceededEvent.MaxFilesExceededHandler<UploadFile> handler) { 743 return addHandler(new MaxFilesExceededEvent.MaxFilesExceededHandler<UploadFile>() { 744 @Override 745 public void onMaxFilesExceeded(MaxFilesExceededEvent<UploadFile> event) { 746 if (isEnabled()) { 747 handler.onMaxFilesExceeded(event); 748 } 749 } 750 }, MaxFilesExceededEvent.getType()); 751 } 752}