org.rest.client.ui.desktop.ResponseViewImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.rest.client.ui.desktop.ResponseViewImpl.java

Source

/*******************************************************************************
 * Copyright 2012 Pawe Pszty
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.rest.client.ui.desktop;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.rest.client.RestClient;
import org.rest.client.dom.worker.WebWorkerError;
import org.rest.client.dom.worker.Worker;
import org.rest.client.dom.worker.WorkerMessageHandler;
import org.rest.client.event.OverwriteUrlEvent;
import org.rest.client.request.RedirectData;
import org.rest.client.storage.store.objects.RequestObject;
import org.rest.client.storage.websql.HeaderRow;
import org.rest.client.ui.ResponseView;
import org.rest.client.ui.desktop.widget.JSONViewer;
import org.rest.client.ui.desktop.widget.RedirectView;
import org.rest.client.ui.desktop.widget.ResponseHeaderLine;
import org.rest.client.ui.desktop.widget.StatusCodeImage;
import org.rest.client.ui.desktop.widget.XMLViewer;
import org.rest.client.ui.html5.HTML5Element;
import org.rest.client.util.JSONHeadersUtils;
import org.rest.client.util.Utils;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.PreElement;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.storage.client.Storage;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ScrollEvent;
import com.google.gwt.user.client.Window.ScrollHandler;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xhr2.client.Header;
import com.google.gwt.xhr2.client.Response;
import com.google.gwt.xhr2.client.XMLHttpRequest2;

public class ResponseViewImpl extends Composite implements ResponseView {
    private static ResponseViewImplUiBinder uiBinder = GWT.create(ResponseViewImplUiBinder.class);

    interface ResponseViewImplUiBinder extends UiBinder<Widget, ResponseViewImpl> {
    }

    class WidgetStyle {
        String error = "Response_View_error";
        String warning = "Response_View_warning";
        String requestError = "Response_View_requestError";
        String responseRow = "Response_View_responseRow";
        String label = "Response_View_label";
        String result = "Response_View_result";
        String onTop = "Response_View_onTop";
        String collapseButton = "androidNavigationCollapse";
        String expandButton = "androidNavigationExpand";
    }

    private ResponsePresenter listener;
    private boolean success = false;
    private Response response;
    private long requestTime;
    private final static String COLLAPSED_REQUEST_HEADERS_KEY = "resp_panel_crqhk";
    private final static String COLLAPSED_RESPONSE_HEADERS_KEY = "resp_panel_crshk";

    @UiField
    StatusCodeImage codeImage;
    @UiField
    InlineLabel loadingTime;
    @UiField
    InlineLabel codeContainer;
    @UiField
    HTMLPanel headersPanel;
    @UiField
    HTMLPanel requestHeadersPanel;
    @UiField
    HTMLPanel responsePayloadContainer;
    @UiField
    HTMLPanel requestHeadersContainer;
    @UiField
    HTMLPanel responseHeadersContainer;
    @UiField
    InlineLabel rawTab;
    @UiField
    InlineLabel xmlTab;
    @UiField
    InlineLabel jsonTab;
    @UiField
    InlineLabel imageTab;
    @UiField
    InlineLabel parsedTab;
    @UiField
    DivElement tabContent;
    @UiField
    HTML plainBody;
    @UiField
    Anchor parsedOpen;
    @UiField
    Anchor forceOpenAsJSON;
    @UiField
    Anchor collapseRequestHeaders;
    @UiField
    Anchor collapseResponseHeaders;
    @UiField
    Anchor forceOpenAsXML;
    @UiField
    PreElement parsedBody;
    @UiField
    HTMLPanel xmlPanel;
    @UiField
    HTMLPanel imagePanel;
    @UiField
    HTMLPanel jsonPanel;
    @UiField
    HTMLPanel redirects;
    @UiField
    DivElement scrollContainer;
    WidgetStyle style = new WidgetStyle();

    public ResponseViewImpl() {
        initWidget(uiBinder.createAndBindUi(this));
        setHeadersPanelCollapsable();
    }

    private void setHeadersPanelCollapsable() {
        requestHeadersContainer.addDomHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                if (collapseRequestHeaders.getStyleName().contains(" visible")) {
                    return;
                }
                collapseRequestHeaders.addStyleName("visible");
            }
        }, MouseOverEvent.getType());

        requestHeadersContainer.addDomHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                collapseRequestHeaders.removeStyleName("visible");
            }
        }, MouseOutEvent.getType());

        responseHeadersContainer.addDomHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                if (collapseResponseHeaders.getStyleName().contains(" visible")) {
                    return;
                }
                collapseResponseHeaders.addStyleName("visible");
            }
        }, MouseOverEvent.getType());

        responseHeadersContainer.addDomHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                collapseResponseHeaders.removeStyleName("visible");
            }
        }, MouseOutEvent.getType());

        Storage sessionStore = Storage.getSessionStorageIfSupported();
        String rqhk = sessionStore.getItem(COLLAPSED_REQUEST_HEADERS_KEY);
        String rshk = sessionStore.getItem(COLLAPSED_RESPONSE_HEADERS_KEY);
        if (rqhk != null && rqhk.equals("1")) {
            requestHeadersContainer.addStyleName("headersCollapsed");
            collapseRequestHeaders.removeStyleName(style.collapseButton);
            collapseRequestHeaders.addStyleName(style.expandButton);
        }
        if (rshk != null && rshk.equals("1")) {
            responseHeadersContainer.addStyleName("headersCollapsed");
            collapseResponseHeaders.removeStyleName(style.collapseButton);
            collapseResponseHeaders.addStyleName(style.expandButton);
        }
    }

    @Override
    public void setPresenter(ResponsePresenter listener) {
        this.listener = listener;
    }

    @Override
    public void setResponseData(boolean success, Response response, long requestTime) {
        this.requestTime = requestTime;
        this.success = success;
        this.response = response;
        handleTabsChange();
        fill();

    }

    private void fill() {
        if (!success || response == null) {
            this.addStyleName(style.requestError);
        } else {
            this.removeStyleName(style.requestError);
        }
        if (response == null) {
            cleanupErrorResponse();
            return;
        }
        setResponseStatus();
        setResponseBody();
    }

    void cleanupErrorResponse() {
        codeContainer.getElement().setInnerHTML("An error occured during the request.");
        codeContainer.getElement().addClassName(style.error);
        loadingTime.setText("0");
        responsePayloadContainer.setVisible(false);
        responseHeadersContainer.setVisible(false);
        requestHeadersContainer.setVisible(false);
        scrollContainer.addClassName(style.onTop);
    }

    private void setResponseStatus() {
        //
        // Set status code
        //
        int code = response.getStatus();
        String msg = response.getStatusText();
        if (code >= 500 || code == 0) {
            codeContainer.getElement().addClassName(style.error);
        } else if (code >= 400 && code < 500) {
            codeContainer.getElement().addClassName(style.warning);
        }
        String txt = "<strong>" + code + "</strong>";
        if (msg != null && !msg.equals("")) {
            txt += " " + msg;
        } else if (code == 0) {
            txt += " NO RESPONSE";
        }
        codeContainer.getElement().setInnerHTML(txt);
        codeImage.setCode(code);

        //
        // Set request time
        //
        loadingTime.getElement().setInnerText(String.valueOf(requestTime));
    }

    @Override
    public void setRequestHeadersExternal(ArrayList<Header> headers) {
        final HashMap<String, ResponseHeaderLine> map = new HashMap<String, ResponseHeaderLine>();
        ArrayList<String> list = new ArrayList<String>();
        if (headers != null) {
            for (Header header : headers) {
                String headerName = header.getName();
                ResponseHeaderLine rhl = new ResponseHeaderLine(header);
                map.put(headerName, rhl);
                list.add(headerName);
                requestHeadersPanel.add(rhl);
            }
        }
        listener.getRequestHeadersInfo(list, new Callback<List<HeaderRow>, Throwable>() {
            @Override
            public void onSuccess(List<HeaderRow> result) {
                for (HeaderRow row : result) {
                    String name = row.getName();
                    if (map.containsKey(name)) {
                        ResponseHeaderLine line = map.get(name);
                        line.updateDesc(row.getDesc());
                        line.updateExample(row.getExample());
                        line.updateName(name);
                    }
                }
            }

            @Override
            public void onFailure(Throwable reason) {
                if (RestClient.isDebug()) {
                    Log.debug("Unable to get request headers help.", reason);
                }
            }
        });
    }

    @Override
    public void setResponseHeadersExternal(ArrayList<Header> headers) {
        final HashMap<String, ResponseHeaderLine> map = new HashMap<String, ResponseHeaderLine>();
        ArrayList<String> list = new ArrayList<String>();
        for (Header header : headers) {
            String headerName = header.getName();
            ResponseHeaderLine rhl = new ResponseHeaderLine(header);
            map.put(headerName, rhl);
            list.add(headerName);
            headersPanel.add(rhl);
        }

        listener.getResponseHeadersInfo(list, new Callback<List<HeaderRow>, Throwable>() {
            @Override
            public void onSuccess(List<HeaderRow> result) {
                for (HeaderRow row : result) {
                    String name = row.getName();
                    if (map.containsKey(name)) {
                        ResponseHeaderLine line = map.get(name);
                        line.updateDesc(row.getDesc());
                        line.updateExample(row.getExample());
                        line.updateName(name);
                    }
                }
            }

            @Override
            public void onFailure(Throwable reason) {
                if (RestClient.isDebug()) {
                    Log.debug("Unable to get response headers help.", reason);
                }
            }
        });
    }

    @Override
    public void setRedirectData(ArrayList<RedirectData> redirectData) {
        if (redirectData == null)
            return;
        boolean addNumber = false;
        int size = redirectData.size();
        if (size > 1)
            addNumber = true;

        for (int i = 0; i < size; i++) {
            RedirectData data = redirectData.get(i);

            FlowPanel wrapper = new FlowPanel();
            wrapper.setStyleName(style.responseRow);
            Label redirectLabel = new Label("Redirect" + (addNumber ? " #" + (i + 1) : ""));
            redirectLabel.setStyleName(style.label);
            wrapper.add(redirectLabel);
            SimplePanel result = new SimplePanel();
            result.setStyleName(style.result);
            wrapper.add(result);
            RedirectView view = new RedirectView(data, listener);
            result.add(view);
            redirects.add(wrapper);
        }
    }

    private void setResponseBody() {
        if (!success || response.getStatus() == 0) {
            return;
        }
        //Response does not contain any data.
        final String body = response.getResponseText();
        Document xml = response.getResponseXML();
        boolean isXML = false, isJSON = false, isImage = false;

        String escaped = SafeHtmlUtils.htmlEscape(body);
        boolean useCodeMirror = false;

        if (xml != null) {
            isXML = true;
        }
        Header[] headers = response.getHeaders();
        if (!isXML) {
            //check if response has JSON header:
            if (isJSONHeader(headers)) {
                isJSON = true;
            }
        }
        if (!isJSON && !isXML) {
            //just HTML
            useCodeMirror = true;
        }

        isImage = isImageHeader(headers);
        if (isImage) {
            useCodeMirror = false;
            isJSON = false;
            isXML = false;
        }

        if (escaped.equals("")) {
            xml = null;
            isXML = false;
            Element info = DOM.createSpan();
            info.setInnerText("Response does not contain any data.");
            info.addClassName("note italic");
            plainBody.getElement().appendChild(info);
            setTabOpened(TABS.RAW, rawTab);
            return;
        } else {
            plainBody.setHTML(escaped);
        }

        if (useCodeMirror) {
            setTabOpened(TABS.PARSED, parsedTab);
            //show this tab
            setTabVisible(TABS.PARSED, parsedTab);

            if (RestClient.isDebug()) {
                Log.debug("Initialize code mirror...");
            }
            String _encoding = getRequestContentType("text/html");
            if (_encoding.contains("javascript")) {
                _encoding = "text/javascript";
            }
            final String encoding = _encoding;

            try {
                getRequestDomainAndPath(new Callback<String, Throwable>() {
                    @Override
                    public void onSuccess(String result) {
                        loadCodeMirror(body, encoding, result);
                    }

                    @Override
                    public void onFailure(Throwable reason) {

                    }
                });

            } catch (Exception e) {
                if (RestClient.isDebug()) {
                    Log.warn("Unable to load CodeMirror.", e);
                }
            }

            //         if(isJavaScriptHeader(headers)){
            forceOpenAsJSON.removeStyleName("hidden");
            //         }
        }
        if (isJSON) {
            setTabOpened(TABS.JSON, jsonTab);
            new JSONViewer(body, jsonPanel);
            setTabVisible(TABS.JSON, jsonTab);
        }
        if (isXML) {
            setTabOpened(TABS.XML, xmlTab);
            new XMLViewer(body, xmlPanel, xml);
            setTabVisible(TABS.XML, xmlTab);
        }
        if (isImage) {

            //         setTabOpened(TABS.IMAGE, imageTab);
            //         setTabVisible(TABS.IMAGE, imageTab);
            //         final Image img = new Image();
            //         img.addLoadHandler(new LoadHandler() {
            //            @Override
            //            public void onLoad(LoadEvent event) {
            //               revokeImageUrl(img.getUrl());
            //            }
            //         });
            //         img.setUrl(createImageUrl(response.getRequest(),"image/png"));
            //         imagePanel.add(img);
        }
        if (RestClient.isDebug()) {
            Log.debug("Response panel has been filled with new data");
        }
    }

    private final native String createImageUrl(XMLHttpRequest2 xhr, String mime) /*-{
                                                                                 var blob = new $wnd.Blob([xhr.response], {type: mime});
                                                                                 return $wnd.URL.createObjectURL(blob);
                                                                                 }-*/;

    private final native void revokeImageUrl(String url) /*-{
                                                         $wnd.URL.revokeObjectURL(url);
                                                         }-*/;

    /**
     * Check if in response headers is some header defined as JSON header.
     * @param headers
     * @return
     */
    private boolean isJSONHeader(Header[] headers) {
        String[] jsonHeadersDefinitions = JSONHeadersUtils.getJSONHeadersListSynch();
        for (Header header : headers) {
            if (header == null) {
                continue;
            }
            String name = header.getName().toLowerCase();
            if (name.equals("content-type")) {
                String value = header.getValue().toLowerCase();
                if (value.contains("+json")) {
                    return true;
                }
                for (String headerDef : jsonHeadersDefinitions) {
                    if (value.contains(headerDef)) {
                        return true;
                    }
                }
                return false;
            }
        }
        return false;
    }

    private boolean isImageHeader(Header[] headers) {
        boolean result = false;
        for (Header header : headers) {
            if (!header.getName().toLowerCase().equals("content-type"))
                continue;
            if (header.getValue().startsWith("image/")) {
                result = true;
            }
        }
        return result;
    }

    private boolean isJavaScriptHeader(Header[] headers) {
        boolean result = false;
        for (Header header : headers) {
            if (!header.getName().toLowerCase().equals("content-type"))
                continue;
            if (header.getValue().contains("javascript")) {
                result = true;
            }
        }
        return result;
    }

    /**
     * Load code mirror library.
     * @param text
     * @param encoding 
     * @throws JavaScriptException
     */
    final native void loadCodeMirror(String text, String encoding, String baseUrl) throws JavaScriptException /*-{
                                                                                                              var elements = [];
                                                                                                              var context = this;
                                                                                                              var clb = $entry(function(a,b) {
                                                                                                              elements.push({string:a,style:b});
                                                                                                              });
                                                                                                              var ready = $entry(function() {
                                                                                                              var result = {
                                                                                                              html: elements,
                                                                                                              url: baseUrl 
                                                                                                              }
                                                                                                              context.@org.rest.client.ui.desktop.ResponseViewImpl::codeMirrorCallback(Lcom/google/gwt/core/client/JavaScriptObject;)(result);
                                                                                                              });
                                                                                                              try{
                                                                                                              $wnd.CodeMirror.runMode(text, encoding, clb, ready);
                                                                                                              } catch(e){
                                                                                                              $wnd.alert("Unable to initialize CodeMirror :( " + e.message);
                                                                                                              }
                                                                                                              }-*/;

    void codeMirrorCallback(JavaScriptObject html) {
        Worker worker = new Worker("/workers/htmlviewer.js");
        worker.onMessage(new WorkerMessageHandler() {
            @Override
            public void onMessage(String message) {
                parsedBody.setInnerHTML(message);
                addNativeControls(parsedBody);
            }

            @Override
            public void onError(WebWorkerError err) {
                parsedBody.setInnerHTML(err.getMessage());
            }
        });
        worker.postMessage(html);
    }

    private void getRequestDomainAndPath(final Callback<String, Throwable> callback) {

        RestClient.collectRequestData(new Callback<RequestObject, Throwable>() {

            @Override
            public void onSuccess(RequestObject result) {
                String url = result.getURL();
                if (!url.contains("://")) { //not FQDN - local (debug?)
                    String root = Window.Location.getProtocol() + "//" + Window.Location.getHost();
                    if (url.startsWith("/")) {
                        url = root + "" + url;
                    } else {
                        url = root + Window.Location.getPath() + url;
                    }
                }
                if (url.contains("?")) {
                    //remove query parameters
                    url = url.substring(0, url.indexOf("?"));
                }
                if (url.contains("#")) {
                    //remove history tokens
                    url = url.substring(0, url.indexOf("#"));
                }
                //remove last path segment
                int lastSlash = url.lastIndexOf("/");
                if (lastSlash > 0) {
                    if (!url.substring(lastSlash - 1, lastSlash).equals("/")) {
                        url = url.substring(0, lastSlash + 1);
                    }
                }
                if (!url.endsWith("/")) {
                    url += "/";
                }
                callback.onSuccess(url);
            }

            @Override
            public void onFailure(Throwable reason) {
                callback.onFailure(reason);
            }
        });
    }

    final void fireUrlChangeEvent(String url) {
        RestClient.getClientFactory().getEventBus().fireEvent(new OverwriteUrlEvent(url));
    }

    private final native void addNativeControls(com.google.gwt.dom.client.Element element)/*-{
                                                                                          var context = this;
                                                                                          element.addEventListener('click', function(e){
                                                                                          if(!e.target) return;
                                                                                          if(e.target.nodeName == "A"){
                                                                                          e.preventDefault();
                                                                                          var url = e.target.getAttribute('href');
                                                                                          context.@org.rest.client.ui.desktop.ResponseViewImpl::fireUrlChangeEvent(Ljava/lang/String;)(url);
                                                                                          $wnd.scrollTo(0,0);
                                                                                          return;
                                                                                          }
                                                                                          }, true);
                                                                                          }-*/;

    public enum TABS {
        RAW("raw"), XML("xml"), JSON("json"), PARSED("parsed"), IMAGE("image");

        private final String type;

        TABS(String type) {
            this.type = type;
        }

        public String toString() {
            return this.type;
        }
    }

    private TABS currentTab = TABS.RAW;

    private void handleTabsChange() {

        //RAW RESPONSE
        rawTab.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (currentTab.equals(TABS.RAW))
                    return;
                setTabOpened(TABS.RAW, rawTab);
            }

        });
        rawTab.addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                HTML5Element tab = (HTML5Element) rawTab.getElement().cast();
                if (!tab.getClassList().contains("inlineButtonChecked"))
                    tab.getClassList().add("inlineButtonHover");
            }
        });
        rawTab.addMouseOutHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                HTML5Element tab = (HTML5Element) rawTab.getElement().cast();
                if (!tab.getClassList().contains("inlineButtonHover"))
                    tab.getClassList().remove("inlineButtonHover");
            }
        });

        //PARSED RESPONSE

        parsedTab.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (currentTab.equals(TABS.PARSED))
                    return;
                setTabOpened(TABS.PARSED, parsedTab);
            }
        });
        parsedTab.addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                HTML5Element tab = (HTML5Element) parsedTab.getElement().cast();
                if (!tab.getClassList().contains("inlineButtonChecked"))
                    tab.getClassList().add("inlineButtonHover");
            }
        });
        parsedTab.addMouseOutHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                HTML5Element tab = (HTML5Element) parsedTab.getElement().cast();
                if (!tab.getClassList().contains("inlineButtonHover"))
                    tab.getClassList().remove("inlineButtonHover");
            }
        });

        //XML RESPONSE
        xmlTab.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (currentTab.equals(TABS.XML))
                    return;
                setTabOpened(TABS.XML, xmlTab);
            }
        });
        xmlTab.addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                HTML5Element tab = (HTML5Element) xmlTab.getElement().cast();
                if (!tab.getClassList().contains("inlineButtonChecked"))
                    tab.getClassList().add("inlineButtonHover");
            }
        });
        xmlTab.addMouseOutHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                HTML5Element tab = (HTML5Element) xmlTab.getElement().cast();
                if (tab.getClassList().contains("inlineButtonHover"))
                    tab.getClassList().remove("inlineButtonHover");
            }
        });

        //JSON RESPONSE
        jsonTab.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (currentTab.equals(TABS.JSON))
                    return;
                setTabOpened(TABS.JSON, jsonTab);
            }
        });
        jsonTab.addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                HTML5Element tab = (HTML5Element) jsonTab.getElement().cast();
                tab.getClassList().add("inlineButtonHover");
            }
        });
        jsonTab.addMouseOutHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                HTML5Element tab = (HTML5Element) jsonTab.getElement().cast();
                if (tab.getClassList().contains("inlineButtonHover"))
                    tab.getClassList().remove("inlineButtonHover");
            }
        });

        //IMAGE RESPONSE
        imageTab.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (currentTab.equals(TABS.IMAGE))
                    return;
                setTabOpened(TABS.IMAGE, imageTab);
            }
        });
        imageTab.addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                HTML5Element tab = (HTML5Element) imageTab.getElement().cast();
                tab.getClassList().add("inlineButtonHover");
            }
        });
        imageTab.addMouseOutHandler(new MouseOutHandler() {
            @Override
            public void onMouseOut(MouseOutEvent event) {
                HTML5Element tab = (HTML5Element) imageTab.getElement().cast();
                if (tab.getClassList().contains("inlineButtonHover"))
                    tab.getClassList().remove("inlineButtonHover");
            }
        });
    }

    private void setTabOpened(TABS type, InlineLabel tabHandler) {
        String tabHandlercurrent = "inlineButtonChecked";
        HTML5Element tab = (HTML5Element) tabHandler.getElement().cast();
        ((HTML5Element) tab.getParentElement()).querySelector("." + tabHandlercurrent).getClassList()
                .remove(tabHandlercurrent);
        tab.getClassList().add(tabHandlercurrent);

        HTML5Element contentParent = (HTML5Element) tabContent.getParentElement();
        contentParent.querySelector(".tabsContent .tabContent.tabContentCurrent").getClassList()
                .remove("tabContentCurrent");
        contentParent.querySelector(".tabsContent .tabContent[data-tab=\"" + type.toString() + "\"]").getClassList()
                .add("tabContentCurrent");

        currentTab = type;
    }

    private void setTabVisible(TABS type, InlineLabel tabHandler) {
        HTML5Element tab = (HTML5Element) tabHandler.getElement().cast();
        tab.getClassList().remove("hidden");
        HTML5Element contentParent = (HTML5Element) tabContent.getParentElement();
        contentParent.querySelector(".tabsContent .tabContent[data-tab=\"" + type.toString() + "\"]").getClassList()
                .remove("hidden");
    }

    @UiHandler("parsedOpen")
    public void onOpen(ClickEvent event) {
        event.preventDefault();
        writeRawBody(response.getResponseText());
    }

    private final native void writeRawBody(String body)/*-{
                                                       var wnd = $wnd.open();
                                                       wnd.document.body.innerHTML = body;
                                                       }-*/;

    private String requestEncoding = null;

    private String getRequestContentType(String defaultEncodeing) {
        if (requestEncoding != null) {
            return requestEncoding;
        }
        Header[] headers = response.getHeaders();
        for (Header header : headers) {
            if (header.getName().toLowerCase().equals("content-type")) {
                defaultEncodeing = header.getValue().split(";")[0];
                break;
            }
        }
        requestEncoding = defaultEncodeing;
        return defaultEncodeing;
    }

    private int initialScrollTop = -1;
    private int initialOffsetTop = -1;
    private final int marginOffset = 25;
    private int lastEndPosition = -1;

    @Override
    public void scrollToView() {
        _scrollIntoView(this.getElement());
        Window.addWindowScrollHandler(new ScrollHandler() {
            @Override
            public void onWindowScroll(ScrollEvent event) {
                int top = event.getScrollTop();

                if (Math.abs(top) < 20) {
                    scrollContainer.addClassName(style.onTop);
                } else {
                    scrollContainer.removeClassName(style.onTop);
                }

                int scrollTop = scrollContainer.getAbsoluteTop();

                int nextPos = initialOffsetTop + marginOffset + (top - initialScrollTop);
                boolean canMove = false;
                if (top + marginOffset > scrollTop) {
                    if (initialOffsetTop == -1) {
                        initialOffsetTop = scrollContainer.getOffsetTop();
                        initialScrollTop = scrollTop;
                        nextPos = initialOffsetTop + marginOffset + (top - initialScrollTop);
                    }
                    canMove = true;
                } else if (nextPos < lastEndPosition) {
                    if (top > initialScrollTop) {
                        canMove = true;
                    } else {
                        if (scrollTop != initialScrollTop) {
                            nextPos = initialOffsetTop;
                            canMove = true;
                        }
                    }
                }
                if (canMove) {
                    lastEndPosition = nextPos;
                    scrollContainer.getStyle().setTop(nextPos, Unit.PX);
                }

            }
        });
    }

    @UiHandler("scrollButton")
    void onscrollButton(ClickEvent e) {
        e.preventDefault();
        _scrollToStart();
    }

    private final native void _scrollToStart() /*-{
                                               $wnd.scrollTo(0,0);
                                               }-*/;

    private final native void _scrollIntoView(Element element) /*-{
                                                               element.scrollIntoView();
                                                               //      element.scrollIntoViewIfNeeded();
                                                               }-*/;

    @UiHandler("wrapContentButton")
    void onWrapContent(ClickEvent e) {
        e.preventDefault();
        Anchor el = (Anchor) e.getSource();
        String whiteSpace = plainBody.getElement().getStyle().getProperty("whiteSpace");
        boolean isWrapped = true;
        if (!whiteSpace.isEmpty()) {
            isWrapped = whiteSpace.equals("pre");
        }

        if (!isWrapped) {
            plainBody.getElement().getStyle().setProperty("whiteSpace", "pre");
            el.setText("Word unwrap");
        } else {
            plainBody.getElement().getStyle().setProperty("whiteSpace", "nowrap");
            el.setText("Word wrap");
        }
    }

    @UiHandler({ "copyClipboardButton", "copyClipboardButton2", "copyClipboardButton3", "copyClipboardButton4" })
    void onCopy(ClickEvent e) {
        e.preventDefault();
        String body = response.getResponseText();
        RestClient.getClientFactory().getChromeMessagePassing().postMessage("copyToClipboard", body);
    }

    @UiHandler({ "saveAsFileButton1", "saveAsFileButton2", "saveAsFileButton3", "saveAsFileButton4" })
    void onSaveAsFileClick(ClickEvent e) {
        final Anchor anchor = (Anchor) e.getSource();
        final Element anchorElement = anchor.getElement();
        String download = anchorElement.getAttribute("download");
        if (download != null && !download.isEmpty()) {
            //already have download.
            if (!anchorElement.getAttribute("disabled").isEmpty()) {
                return;
            }
            anchorElement.setAttribute("disabled", "true");
            Timer t = new Timer() {
                @Override
                public void run() {
                    anchor.setHref("about:blank");
                    anchor.setText("Save as file");
                    anchorElement.removeAttribute("download");
                    anchorElement.removeAttribute("data-downloadurl");
                    anchorElement.removeAttribute("disabled");
                    listener.revokeDownloadData();
                }
            };
            t.schedule(1500);
            return;
        }
        e.preventDefault();

        final String body = response.getResponseText();
        final String encoding = getRequestContentType("text/html");

        Scheduler.get().scheduleDeferred(new Command() {
            public void execute() {
                String ext = Utils.guessFileExtension(encoding);
                String fileObjectUrl = listener.createDownloadData(body, encoding);
                anchor.setHref(fileObjectUrl);
                String date = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM).format(new Date());
                String fileName = "arc-response-" + date + "." + ext;
                anchorElement.setAttribute("download", fileName);
                anchorElement.setAttribute("data-downloadurl", encoding + ":" + fileName + ":" + fileObjectUrl);
                anchor.setText("Download");
            }
        });
    }

    @UiHandler("forceOpenAsJSON")
    void onForceOpenAsJSON(ClickEvent e) {
        e.preventDefault();
        forceOpenAsJSON.addStyleName("hidden");
        final String body = response.getResponseText();
        setTabOpened(TABS.JSON, jsonTab);
        new JSONViewer(body, jsonPanel);
        setTabVisible(TABS.JSON, jsonTab);
    }

    @UiHandler("collapseResponseHeaders")
    void onCollapseResponseHeaders(ClickEvent e) {
        e.preventDefault();
        Storage sessionStore = Storage.getSessionStorageIfSupported();
        if (responseHeadersContainer.getStyleName().contains("headersCollapsed")) {
            responseHeadersContainer.removeStyleName("headersCollapsed");
            collapseResponseHeaders.removeStyleName(style.expandButton);
            collapseResponseHeaders.addStyleName(style.collapseButton);
            sessionStore.setItem(COLLAPSED_RESPONSE_HEADERS_KEY, "0");
        } else {
            responseHeadersContainer.addStyleName("headersCollapsed");
            collapseResponseHeaders.removeStyleName(style.collapseButton);
            collapseResponseHeaders.addStyleName(style.expandButton);
            sessionStore.setItem(COLLAPSED_RESPONSE_HEADERS_KEY, "1");
        }
    }

    @UiHandler("collapseRequestHeaders")
    void onCollapseRequestHeaders(ClickEvent e) {
        e.preventDefault();
        Storage sessionStore = Storage.getSessionStorageIfSupported();
        if (requestHeadersContainer.getStyleName().contains("headersCollapsed")) {
            requestHeadersContainer.removeStyleName("headersCollapsed");
            collapseRequestHeaders.removeStyleName(style.expandButton);
            collapseRequestHeaders.addStyleName(style.collapseButton);
            sessionStore.setItem(COLLAPSED_REQUEST_HEADERS_KEY, "0");
        } else {
            requestHeadersContainer.addStyleName("headersCollapsed");
            collapseRequestHeaders.removeStyleName(style.collapseButton);
            collapseRequestHeaders.addStyleName(style.expandButton);
            sessionStore.setItem(COLLAPSED_REQUEST_HEADERS_KEY, "1");
        }

    }

    @UiHandler("forceOpenAsXML")
    void onForceOpenAsXML(ClickEvent e) {
        e.preventDefault();
        forceOpenAsXML.addStyleName("hidden");
        final String body = response.getResponseText();
        setTabOpened(TABS.JSON, xmlTab);
        new XMLViewer(body, xmlPanel, response.getResponseXML());
        setTabVisible(TABS.JSON, xmlTab);
    }
}