/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE file at the root of the source
* tree and available online at
*
* https://github.com/keeps/roda
*/
package org.roda.wui.client.browse;
import org.apache.commons.httpclient.HttpStatus;
import org.roda.core.data.v2.index.IsIndexed;
import org.roda.core.data.v2.ip.IndexedDIP;
import org.roda.core.data.v2.ip.metadata.FileFormat;
import org.roda.wui.client.common.utils.JavascriptUtils;
import org.roda.wui.client.common.utils.StringUtils;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.media.client.Audio;
import com.google.gwt.media.client.Video;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import config.i18n.client.ClientMessages;
public class BitstreamPreview<T extends IsIndexed> extends Composite {
private static final String VIEWER_TYPE_VIDEO = "video";
private static final String VIEWER_TYPE_AUDIO = "audio";
private static final String VIEWER_TYPE_TEXT = "text";
private static final String VIEWER_TYPE_PDF = "pdf";
private static final String VIEWER_TYPE_IMAGE = "image";
private static final ClientMessages messages = GWT.create(ClientMessages.class);
// interface
private final FlowPanel panel;
// system properties
private final Viewers viewers;
// bitstream properties
private final SafeUri bitstreamDownloadUri;
private final FileFormat format;
private final String filename;
private final long size;
private final boolean isDirectory;
// other
private final Command onPreviewFailure;
private final T object;
public BitstreamPreview(Viewers viewers, SafeUri bitstreamDownloadUri, FileFormat format, String filename, long size,
boolean isDirectory, T object) {
this(viewers, bitstreamDownloadUri, format, filename, size, isDirectory, new Command() {
@Override
public void execute() {
// do nothing
}
}, object);
}
public BitstreamPreview(Viewers viewers, SafeUri bitstreamDownloadUri, FileFormat format, String filename, long size,
boolean isDirectory, Command onPreviewFailure, T object) {
super();
this.object = object;
this.panel = new FlowPanel();
this.viewers = viewers;
this.bitstreamDownloadUri = bitstreamDownloadUri;
this.format = format;
this.filename = filename;
this.size = size;
this.isDirectory = isDirectory;
this.onPreviewFailure = onPreviewFailure;
initWidget(panel);
setStyleName("bitstreamPreview");
if (isDirectory) {
addStyleDependentName("directory");
}
init();
}
public SafeUri getBitstreamDownloadUri() {
return bitstreamDownloadUri;
}
public FileFormat getFormat() {
return format;
}
public String getFilename() {
return filename;
}
public long getSize() {
return size;
}
public boolean isDirectory() {
return isDirectory;
}
private void init() {
if (!isDirectory) {
String type = viewerType();
if (type != null) {
if (type.equals(VIEWER_TYPE_IMAGE)) {
imagePreview();
} else if (type.equals(VIEWER_TYPE_PDF)) {
pdfPreview();
} else if (type.equals(VIEWER_TYPE_TEXT)) {
textPreview();
} else if (type.equals(VIEWER_TYPE_AUDIO)) {
audioPreview();
} else if (type.equals(VIEWER_TYPE_VIDEO)) {
videoPreview();
} else {
notSupportedPreview();
}
} else if (object instanceof IndexedDIP) {
IndexedDIP dip = (IndexedDIP) object;
dipUrlPreview(dip);
} else {
notSupportedPreview();
}
} else {
panel.add(directoryPreview());
}
}
private String viewerType() {
String type = null;
if (format != null) {
if (format.getPronom() != null) {
type = viewers.getPronoms().get(format.getPronom());
}
if (format.getMimeType() != null && type == null) {
type = viewers.getMimetypes().get(format.getMimeType());
}
}
if (type == null && filename.lastIndexOf('.') != -1) {
String extension = getFileNameExtension();
type = viewers.getExtensions().get(extension);
}
return type;
}
private String getFileNameExtension() {
return filename.substring(filename.lastIndexOf('.')).toLowerCase();
}
private void imagePreview() {
final SimplePanel imageContainer = new SimplePanel();
panel.add(imageContainer);
imageContainer.setStyleName("viewRepresentationImageFilePreview");
imageContainer.addAttachHandler(new Handler() {
private JavaScriptObject imageViewer = null;
@Override
public void onAttachOrDetach(AttachEvent event) {
if (event.isAttached()) {
// load image
imageViewer = JavascriptUtils.runImageViewerOn(imageContainer.getElement(), bitstreamDownloadUri.asString());
} else {
// destroy
if (imageViewer != null) {
JavascriptUtils.stopImageViewer(imageViewer);
}
}
}
});
}
private void pdfPreview() {
String viewerHtml = GWT.getHostPageBaseURL() + "pdf/viewer.html?file="
+ encode(GWT.getHostPageBaseURL() + bitstreamDownloadUri.asString());
Frame frame = new Frame(viewerHtml);
panel.add(frame);
frame.setStyleName("viewRepresentationPDFFilePreview");
}
private void textPreview() {
if (StringUtils.isBlank(viewers.getTextLimit()) || size <= Long.parseLong(viewers.getTextLimit())) {
RequestBuilder request = new RequestBuilder(RequestBuilder.GET, bitstreamDownloadUri.asString());
try {
request.sendRequest(null, new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
if (response.getStatusCode() == HttpStatus.SC_OK) {
HTML html = new HTML("<pre><code>" + SafeHtmlUtils.htmlEscape(response.getText()) + "</code></pre>");
FlowPanel frame = new FlowPanel();
frame.add(html);
panel.add(frame);
frame.setStyleName("viewRepresentationTextFilePreview");
JavascriptUtils.runHighlighter(html.getElement());
} else {
errorPreview();
}
}
@Override
public void onError(Request request, Throwable exception) {
errorPreview();
}
});
} catch (RequestException e) {
errorPreview();
}
} else {
errorPreview(messages.viewRepresentationTooLargeErrorPreview());
}
}
private void audioPreview() {
Audio audioPlayer = Audio.createIfSupported();
if (audioPlayer != null) {
HTML html = new HTML();
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.append(SafeHtmlUtils.fromSafeConstant("<i class='fa fa-headphones fa-5'></i>"));
html.setHTML(b.toSafeHtml());
// TODO check if audio source type needs to be transformed
// TODO check if audio player supports provided file format
audioPlayer.addSource(bitstreamDownloadUri.asString(), getAudioSourceType());
audioPlayer.setControls(true);
panel.add(html);
panel.add(audioPlayer);
audioPlayer.addStyleName("viewRepresentationAudioFilePreview");
html.addStyleName("viewRepresentationAudioFilePreviewHTML");
} else {
notSupportedPreview();
}
}
private void videoPreview() {
Video videoPlayer = Video.createIfSupported();
if (videoPlayer != null) {
videoPlayer.addSource(bitstreamDownloadUri.asString(), getVideoSourceType());
videoPlayer.setControls(true);
panel.add(videoPlayer);
videoPlayer.addStyleName("viewRepresentationAudioFilePreview");
} else {
notSupportedPreview();
}
}
private String getVideoSourceType() {
String ret;
if (format != null && StringUtils.isNotBlank(format.getMimeType())) {
String mimetype = format.getMimeType();
if ("application/mp4".equals(mimetype)) {
ret = "video/mp4";
} else if ("application/ogg".equals(mimetype)) {
ret = "video/ogg";
} else {
ret = mimetype;
}
} else {
String extension = getFileNameExtension();
if (".ogg".equals(extension)) {
ret = "video/ogg";
} else {
ret = "video/mp4";
}
}
// TODO video player might not support provided file format
return ret;
}
private String getAudioSourceType() {
String ret;
if (format != null && StringUtils.isNotBlank(format.getMimeType())) {
String mimetype = format.getMimeType();
ret = mimetype;
} else {
String extension = getFileNameExtension();
if (".ogg".equals(extension)) {
ret = "audio/ogg";
} else {
ret = "audio/mpeg";
}
}
// TODO audio player might not support provided file format
return ret;
}
private void dipUrlPreview(IndexedDIP dip) {
Frame frame = new Frame(dip.getOpenExternalURL());
frame.setStyleName("viewDIPPreview");
frame.setTitle(dip.getTitle());
panel.add(frame);
}
private String encode(String string) {
return string.replace("?", "%3F").replace("=", "%3D");
}
private void errorPreview() {
errorPreview(messages.viewRepresentationErrorPreview());
}
private void errorPreview(String errorPreview) {
HTML html = new HTML();
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.append(SafeHtmlUtils.fromSafeConstant("<i class='fa fa-download fa-5'></i>"));
b.append(SafeHtmlUtils.fromSafeConstant("<h4 class='errormessage'>"));
b.append(SafeHtmlUtils.fromString(errorPreview));
b.append(SafeHtmlUtils.fromSafeConstant("</h4>"));
Button downloadButton = new Button(messages.viewRepresentationDownloadFileButton());
downloadButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
downloadFile();
}
});
html.setHTML(b.toSafeHtml());
panel.add(html);
panel.add(downloadButton);
html.setStyleName("viewRepresentationErrorPreview");
downloadButton.setStyleName("btn btn-donwload viewRepresentationNotSupportedDownloadButton");
onPreviewFailure.execute();
}
private void notSupportedPreview() {
HTML html = new HTML();
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.append(SafeHtmlUtils.fromSafeConstant("<i class='fa fa-picture-o fa-5'></i>"));
b.append(SafeHtmlUtils.fromSafeConstant("<h4 class='errormessage'>"));
b.append(SafeHtmlUtils.fromString(messages.viewRepresentationNotSupportedPreview()));
b.append(SafeHtmlUtils.fromSafeConstant("</h4>"));
Button downloadButton = new Button(messages.viewRepresentationDownloadFileButton());
downloadButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
downloadFile();
}
});
html.setHTML(b.toSafeHtml());
panel.add(html);
panel.add(downloadButton);
html.setStyleName("viewRepresentationNotSupportedPreview");
downloadButton.setStyleName("btn btn-download viewRepresentationNotSupportedDownloadButton");
onPreviewFailure.execute();
}
protected Widget directoryPreview() {
HTML html = new HTML();
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.append(SafeHtmlUtils.fromSafeConstant("<i class='fa fa-folder-open fa-5'></i>"));
b.append(SafeHtmlUtils.fromSafeConstant("<h4 class='emptymessage'>"));
b.append(SafeHtmlUtils.fromString(filename + " /"));
b.append(SafeHtmlUtils.fromSafeConstant("</h4>"));
html.setHTML(b.toSafeHtml());
html.setStyleName("viewRepresentationEmptyPreview");
return html;
}
private void downloadFile() {
if (bitstreamDownloadUri != null) {
Window.Location.assign(bitstreamDownloadUri.asString());
}
}
public T getObject() {
return object;
}
}