//  [java]
	private Component newUploadPanel() {
		Fragment fragment;

		IModel<Collection<FileUpload>> model = new PropertyModel<Collection<FileUpload>>(this, "uploads");
		String acceptedFiles;
		if (isImage)
			acceptedFiles = "image/*";
		else
			acceptedFiles = null;
		
		AttachmentSupport attachmentSupport = markdownEditor.getAttachmentSupport();
		if (attachmentSupport != null) {
			fragment = new Fragment(CONTENT_ID, "uploadAttachmentFrag", this);
			
			Form<?> form = new Form<Void>("form") {

				@Override
				protected void onSubmit() {
					super.onSubmit();
					
					AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class);
					String attachmentName;
					FileUpload upload = uploads.iterator().next();
					try (InputStream is = upload.getInputStream()) {
						attachmentName = attachmentSupport.saveAttachment(upload.getClientFileName(), is);
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
					markdownEditor.insertUrl(target, isImage, 
							attachmentSupport.getAttachmentUrl(attachmentName), UrlUtils.describe(attachmentName), null);
					onClose(target);
				}

				@Override
				protected void onFileUploadException(FileUploadException e, Map<String, Object> model) {
					throw new RuntimeException(e);
				}
				
			};
			form.setMaxSize(Bytes.bytes(attachmentSupport.getAttachmentMaxSize()));
			form.setMultiPart(true);
			form.add(new FencedFeedbackPanel("feedback", form));
			
			int maxFilesize = (int) (attachmentSupport.getAttachmentMaxSize()/1024/1024);
			if (maxFilesize <= 0)
				maxFilesize = 1;
			form.add(new DropzoneField("file", model, acceptedFiles, 1, maxFilesize)
					.setRequired(true).setLabel(Model.of("Attachment")));
			
			form.add(new AjaxButton("insert"){});
			
			fragment.add(form);
		} else {
			fragment = new Fragment(CONTENT_ID, "uploadBlobFrag", this);
			Form<?> form = new Form<Void>("form");
			form.setMultiPart(true);
			form.setFileMaxSize(Bytes.megabytes(Project.MAX_UPLOAD_SIZE));
			add(form);
			
			FencedFeedbackPanel feedback = new FencedFeedbackPanel("feedback", form);
			feedback.setOutputMarkupPlaceholderTag(true);
			form.add(feedback);
			
			form.add(new DropzoneField("file", model, acceptedFiles, 1, Project.MAX_UPLOAD_SIZE)
					.setRequired(true).setLabel(Model.of("Attachment")));

			form.add(new TextField<String>("directory", new IModel<String>() {

				@Override
				public void detach() {
				}

				@Override
				public String getObject() {
					return WebSession.get().getMetaData(UPLOAD_DIRECTORY);
				}

				@Override
				public void setObject(String object) {
					WebSession.get().setMetaData(UPLOAD_DIRECTORY, object);
				}
				
			})); 

			BlobRenderContext context = Preconditions.checkNotNull(markdownEditor.getBlobRenderContext());
			ObjectId commitId = resolveCommitId(context);
			Set<BlobIdent> folderPickerState = getPickerState(commitId, context.getBlobIdent(), 
					WebSession.get().getMetaData(FOLDER_PICKER_STATE));
			
			form.add(new DropdownLink("select") {

				@Override
				protected Component newContent(String id, FloatingPanel dropdown) {
					return new BlobFolderPicker(id, commitId) {

						@Override
						protected void onSelect(AjaxRequestTarget target, BlobIdent blobIdent) {
							dropdown.close();
							
							String relativePath = PathUtils.relativize(context.getDirectory(), blobIdent.path);
							String script = String.format("$('form.upload-blob .directory input').val('%s');", 
									JavaScriptEscape.escapeJavaScript(relativePath));
							target.appendJavaScript(script);
						}

						@Override
						protected Project getProject() {
							return markdownEditor.getBlobRenderContext().getProject();
						}

						@Override
						protected void onStateChange() {
							HashSet<String> expandedPaths = new HashSet<>();
							for (BlobIdent blobIdent: folderPickerState)
								expandedPaths.add(blobIdent.path);
							WebSession.get().setMetaData(FOLDER_PICKER_STATE, expandedPaths);
						}

						@Override
						protected Set<BlobIdent> getState() {
							return folderPickerState;
						}
						
					};
				}
				
			});
			
			ReferenceInputBehavior behavior = new ReferenceInputBehavior(true) {
				
				@Override
				protected Project getProject() {
					return markdownEditor.getBlobRenderContext().getProject();
				}
				
			};
			form.add(new TextField<String>("summaryCommitMessage", 
					new PropertyModel<String>(this, "summaryCommitMessage")).add(behavior));
			
			behavior = new ReferenceInputBehavior(true) {
				
				@Override
				protected Project getProject() {
					return markdownEditor.getBlobRenderContext().getProject();
				}
				
			};
			form.add(new TextArea<String>("detailCommitMessage", 
					new PropertyModel<String>(this, "detailCommitMessage")).add(behavior));
			
			form.add(new AjaxButton("insert") {

				@Override
				protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
					super.onSubmit(target, form);

					BlobRenderContext context = Preconditions.checkNotNull(markdownEditor.getBlobRenderContext());
					String commitMessage = summaryCommitMessage;
					if (StringUtils.isBlank(commitMessage))
						commitMessage = "Add files via upload";
					
					if (StringUtils.isNotBlank(detailCommitMessage))
						commitMessage += "\n\n" + detailCommitMessage;

					try {
						String directory = WebSession.get().getMetaData(UPLOAD_DIRECTORY);
						context.onCommitted(null, context.uploadFiles(uploads, directory, commitMessage), null);
						
						String fileName = uploads.iterator().next().getClientFileName();
						String url;
						if (directory != null) 
							url = directory + "/" + UrlUtils.encodePath(fileName);
						else 
							url = UrlUtils.encodePath(fileName);
						markdownEditor.insertUrl(target, isImage, url, UrlUtils.describe(fileName), null);
						onClose(target);
					} catch (GitException e) {
						form.error(e.getMessage());
						target.add(feedback);
					}
				}

				@Override
				protected void onError(AjaxRequestTarget target, Form<?> form) {
					super.onError(target, form);
					target.add(feedback);
				}
				
			});
			
			fragment.add(form);
		}
		
		fragment.setOutputMarkupId(true);
		return fragment;
	}