/*
* JavaScriptEventHistory.java
*
* Copyright (C) 2009-17 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.studio.client;
import java.util.LinkedList;
import java.util.Queue;
import org.rstudio.core.client.widget.MiniPopupPanel;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.inject.Singleton;
// This class maintains a simple JavaScript event history, primarily to be used
// by classes that need to know what sequence of unfortunate events might have
// led to the current application state (whatever that might be).
@Singleton
public class JavaScriptEventHistory
{
public static class EventData extends JavaScriptObject
{
protected EventData()
{
}
public static final native EventData create(NativeEvent event)
/*-{
return {
"type" : event.type || "",
"button" : event.button || -1
};
}-*/;
public final native String getType() /*-{ return this["type"]; }-*/;
public final native int getButton() /*-{ return this["button"]; }-*/;
public static final int BUTTON_NONE = -1;
public static final int BUTTON_LEFT = 0;
public static final int BUTTON_MIDDLE = 1;
public static final int BUTTON_RIGHT = 2;
}
public interface Predicate
{
public boolean accept(EventData event);
}
public JavaScriptEventHistory()
{
queue_ = new LinkedList<EventData>();
registerEventListeners();
}
private final native void registerEventListeners()
/*-{
var self = this;
// get reference to document body
var document = $doc;
var body = document.body;
// define our handler (we can just use a single one)
var handler = $entry(function(event) {
self.@org.rstudio.studio.client.JavaScriptEventHistory::onEvent(Lcom/google/gwt/dom/client/NativeEvent;)(event);
});
// define the events that we want to listen to
var events = [
"cut", "copy", "paste",
"resize", "scroll",
"keydown", "keypress", "keyup",
"mouseenter", "mouseleave", "mouseover", "mousemove", "mouseout",
"mousedown", "mouseup", "click", "dblclick", "contextmenu", "wheel",
"drag", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "drop"
];
// iterate through all keys on body object, and
// register handlers for any prefixed with 'on'
for (var i = 0, n = events.length; i < n; i++) {
body.addEventListener(events[i], handler, true)
}
}-*/;
private void onEvent(NativeEvent event)
{
EventData eventData = EventData.create(event);
queue_.add(eventData);
if (queue_.size() > QUEUE_LENGTH)
queue_.remove();
}
public EventData findEvent(Predicate predicate)
{
for (EventData data : queue_)
if (predicate.accept(data))
return data;
return null;
}
// primarily for debug use (see what events are being emitted over time)
private MiniPopupPanel historyPanel_ = null;
public void debugHistory()
{
if (historyPanel_ == null)
historyPanel_ = new MiniPopupPanel(false, false);
VerticalPanel contentPanel = new VerticalPanel();
contentPanel.add(new HTML("<h4 style='margin: 0;'>JavaScript Event History</h2><hr />"));
int i = 0, n = Math.min(10, queue_.size());
for (EventData event : queue_)
{
if (i++ == n)
break;
contentPanel.add(new HTML("Event: " + event.getType()));
}
historyPanel_.setWidget(contentPanel);
if (!historyPanel_.isShowing())
{
historyPanel_.setPopupPosition(10, 10);
historyPanel_.show();
}
}
private final Queue<EventData> queue_;
private static final int QUEUE_LENGTH = 10;
}