http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
 
b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
new file mode 100644
index 0000000..423a745
--- /dev/null
+++ 
b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.giraph.debugger.Integrity.MessageIntegrityViolation;
+import 
org.apache.giraph.debugger.Integrity.MessageIntegrityViolation.ExtendedOutgoingMessage;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * A wrapper class around the contents of MessageIntegrityViolation inside
+ * integrity.proto. In scenario.proto most things are stored as serialized byte
+ * arrays and this class gives them access through the java classes that those
+ * byte arrays serialize.
+ *
+ * @param <I>
+ *          vertex ID class.
+ * @param <M2>
+ *          outgoing message class.
+ *
+ * author Semih Salihoglu
+ */
+@SuppressWarnings("rawtypes")
+public class MsgIntegrityViolationWrapper<I extends WritableComparable,
+  M2 extends Writable>
+  extends BaseScenarioAndIntegrityWrapper<I> {
+
+  /**
+   * Outgoing message class.
+   */
+  private Class<M2> outgoingMessageClass;
+  /**
+   * List of captured outgoing messages.
+   */
+  private final List<ExtendedOutgoingMessageWrapper>
+  extendedOutgoingMessageWrappers = new ArrayList<>();
+  /**
+   * The superstep number at which these message violations were found.
+   */
+  private long superstepNo;
+
+  /**
+   * Empty constructor to be used for loading from HDFS.
+   */
+  public MsgIntegrityViolationWrapper() {
+  }
+
+  /**
+   * Constructor with field values.
+   *
+   * @param vertexIdClass Vertex id class.
+   * @param outgoingMessageClass Outgoing message class.
+   */
+  public MsgIntegrityViolationWrapper(Class<I> vertexIdClass,
+    Class<M2> outgoingMessageClass) {
+    initialize(vertexIdClass, outgoingMessageClass);
+  }
+
+  /**
+   * Initializes this instance.
+   *
+   * @param vertexIdClass Vertex id class.
+   * @param outgoingMessageClass Outgoing message class.
+   */
+  private void initialize(Class<I> vertexIdClass, Class<M2>
+  outgoingMessageClass) {
+    super.initialize(vertexIdClass);
+    this.outgoingMessageClass = outgoingMessageClass;
+  }
+
+  public Collection<ExtendedOutgoingMessageWrapper>
+  getExtendedOutgoingMessageWrappers() {
+    return extendedOutgoingMessageWrappers;
+  }
+
+  /**
+   * Captures an outgoing message.
+   *
+   * @param srcId Sending vertex id.
+   * @param destinationId Receiving vertex id.
+   * @param message The message being sent to capture.
+   */
+  public void addMsgWrapper(I srcId, I destinationId, M2 message) {
+    extendedOutgoingMessageWrappers.add(new ExtendedOutgoingMessageWrapper(
+      DebuggerUtils.makeCloneOf(srcId, vertexIdClass), DebuggerUtils
+        .makeCloneOf(destinationId, vertexIdClass), DebuggerUtils.makeCloneOf(
+          message, outgoingMessageClass)));
+  }
+
+  /**
+   * @return The number of captured messages so far.
+   */
+  public int numMsgWrappers() {
+    return extendedOutgoingMessageWrappers.size();
+  }
+
+  public Class<M2> getOutgoingMessageClass() {
+    return outgoingMessageClass;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append(super.toString());
+    stringBuilder.append("\noutgoingMessageClass: " +
+      getOutgoingMessageClass().getCanonicalName());
+    for (ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper :
+      getExtendedOutgoingMessageWrappers()) {
+      stringBuilder.append("\n" + extendedOutgoingMessageWrapper);
+    }
+    return stringBuilder.toString();
+  }
+
+  /**
+   * Class for capturing outgoing messages as well as the sending vertex id.
+   */
+  public class ExtendedOutgoingMessageWrapper extends BaseWrapper {
+    /**
+     * Sending vertex id.
+     */
+    private I srcId;
+    /**
+     * Receiving vertex id.
+     */
+    private I destinationId;
+    /**
+     * Message being sent.
+     */
+    private M2 message;
+
+    /**
+     * Constructor with field values.
+     *
+     * @param srcId Sending vertex id.
+     * @param destinationId Receiving vertex id.
+     * @param message Message being sent.
+     */
+    public ExtendedOutgoingMessageWrapper(I srcId, I destinationId, M2 message)
+    {
+      this.setSrcId(srcId);
+      this.setDestinationId(destinationId);
+      this.setMessage(message);
+    }
+
+    /**
+     * Default constructor.
+     */
+    public ExtendedOutgoingMessageWrapper() {
+    }
+
+    @Override
+    public String toString() {
+      return "extendedOutgoingMessage: srcId: " + getSrcId() +
+        " destinationId: " + getDestinationId() + " message: " + getMessage();
+    }
+
+    @Override
+    public GeneratedMessage buildProtoObject() {
+      ExtendedOutgoingMessage.Builder extendedOutgoingMessageBuilder =
+        ExtendedOutgoingMessage.newBuilder();
+      extendedOutgoingMessageBuilder.setSrcId(toByteString(getSrcId()));
+      extendedOutgoingMessageBuilder
+        .setDestinationId(toByteString(getDestinationId()));
+      extendedOutgoingMessageBuilder.setMsgData(toByteString(getMessage()));
+      return extendedOutgoingMessageBuilder.build();
+    }
+
+    @Override
+    public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+      throws IOException {
+      return ExtendedOutgoingMessage.parseFrom(inputStream);
+    }
+
+    @Override
+    public void loadFromProto(GeneratedMessage generatedMessage)
+      throws ClassNotFoundException, IOException, InstantiationException,
+      IllegalAccessException {
+      ExtendedOutgoingMessage extendedOutgoingMessage =
+        (ExtendedOutgoingMessage) generatedMessage;
+      this.setSrcId(DebuggerUtils.newInstance(vertexIdClass));
+      fromByteString(extendedOutgoingMessage.getSrcId(), this.getSrcId());
+      this.setDestinationId(DebuggerUtils.newInstance(vertexIdClass));
+      fromByteString(extendedOutgoingMessage.getDestinationId(),
+        this.getDestinationId());
+      this.setMessage(DebuggerUtils.newInstance(outgoingMessageClass));
+      fromByteString(extendedOutgoingMessage.getMsgData(), this.getMessage());
+    }
+
+    /**
+     * @return the srcId
+     */
+    public I getSrcId() {
+      return srcId;
+    }
+
+    /**
+     * @param srcId the srcId to set
+     */
+    public void setSrcId(I srcId) {
+      this.srcId = srcId;
+    }
+
+    /**
+     * @return the destinationId
+     */
+    public I getDestinationId() {
+      return destinationId;
+    }
+
+    /**
+     * @param destinationId the destinationId to set
+     */
+    public void setDestinationId(I destinationId) {
+      this.destinationId = destinationId;
+    }
+
+    /**
+     * @return the message
+     */
+    public M2 getMessage() {
+      return message;
+    }
+
+    /**
+     * @param message the message to set
+     */
+    public void setMessage(M2 message) {
+      this.message = message;
+    }
+  }
+
+  public long getSuperstepNo() {
+    return superstepNo;
+  }
+
+  public void setSuperstepNo(long superstepNo) {
+    this.superstepNo = superstepNo;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    MessageIntegrityViolation.Builder messageIntegrityViolationBuilder =
+      MessageIntegrityViolation.newBuilder();
+    messageIntegrityViolationBuilder.setVertexIdClass(getVertexIdClass()
+      .getName());
+    messageIntegrityViolationBuilder
+      .setOutgoingMessageClass(getOutgoingMessageClass().getName());
+    messageIntegrityViolationBuilder.setSuperstepNo(getSuperstepNo());
+    for (ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper :
+      extendedOutgoingMessageWrappers) {
+      messageIntegrityViolationBuilder
+        .addMessage((ExtendedOutgoingMessage) extendedOutgoingMessageWrapper
+          .buildProtoObject());
+    }
+    return messageIntegrityViolationBuilder.build();
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public void loadFromProto(GeneratedMessage generatedMessage)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    MessageIntegrityViolation msgIntegrityViolation =
+      (MessageIntegrityViolation) generatedMessage;
+    Class<I> vertexIdClass = (Class<I>) castClassToUpperBound(
+      Class.forName(msgIntegrityViolation.getVertexIdClass()),
+      WritableComparable.class);
+
+    Class<M2> outgoingMessageClazz = (Class<M2>) castClassToUpperBound(
+      Class.forName(msgIntegrityViolation.getOutgoingMessageClass()),
+      Writable.class);
+
+    initialize(vertexIdClass, outgoingMessageClazz);
+    setSuperstepNo(msgIntegrityViolation.getSuperstepNo());
+
+    for (ExtendedOutgoingMessage extendOutgoingMessage : msgIntegrityViolation
+      .getMessageList()) {
+      ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper = new
+        ExtendedOutgoingMessageWrapper();
+      extendedOutgoingMessageWrapper.loadFromProto(extendOutgoingMessage);
+      extendedOutgoingMessageWrappers.add(extendedOutgoingMessageWrapper);
+    }
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return MessageIntegrityViolation.parseFrom(inputStream);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
 
b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
new file mode 100644
index 0000000..b5a9125
--- /dev/null
+++ 
b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * Utility classes for Giraph debugger, Graft.
+ */
+package org.apache.giraph.debugger.utils;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/protobuf/giraph_aggregator.proto
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/protobuf/giraph_aggregator.proto 
b/giraph-debugger/src/main/protobuf/giraph_aggregator.proto
new file mode 100644
index 0000000..7539865
--- /dev/null
+++ b/giraph-debugger/src/main/protobuf/giraph_aggregator.proto
@@ -0,0 +1,12 @@
+package org.apache.giraph.debugger;
+
+message Aggregator {
+  required string aggregatorClass = 1;
+  required AggregatedValue aggregatedValue = 2;
+}
+
+message AggregatedValue { 
+  required string writableClass        = 1;
+  required string key = 2;
+  required bytes value = 3;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/protobuf/integrity.proto
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/protobuf/integrity.proto 
b/giraph-debugger/src/main/protobuf/integrity.proto
new file mode 100644
index 0000000..8d05b64
--- /dev/null
+++ b/giraph-debugger/src/main/protobuf/integrity.proto
@@ -0,0 +1,18 @@
+package org.apache.giraph.debugger;
+
+// Stores a list of messages that are violating an
+// integrity constraint designed by the user.
+message MessageIntegrityViolation {
+  required string vertexIdClass = 1;
+  required string outgoingMessageClass = 2; 
+  required int64 superstepNo = 3;
+  // Called Extended because in scenario.proto there is another
+  // message called OutgoingMessage, which does not have the srcId.
+  repeated ExtendedOutgoingMessage message = 4;
+
+  message ExtendedOutgoingMessage {
+    required bytes srcId = 1;
+    required bytes destinationId = 2;
+    required bytes msgData = 3;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/protobuf/scenario.proto
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/protobuf/scenario.proto 
b/giraph-debugger/src/main/protobuf/scenario.proto
new file mode 100644
index 0000000..42a47a3d
--- /dev/null
+++ b/giraph-debugger/src/main/protobuf/scenario.proto
@@ -0,0 +1,81 @@
+package org.apache.giraph.debugger;
+
+import "giraph_aggregator.proto";
+
+// GiraphVertexScenario captures necessary information to replicate
+// the environment under which user's Computation.compute() function was
+// called on a particular vertex and superstep.
+message GiraphVertexScenario {  
+  required VertexScenarioClasses vertexScenarioClasses = 1; 
+  required VertexContext context = 2;
+  optional Exception exception = 3;
+
+  // VertexScenarioClasses contains the names of the user's Computation,
+  // Vertex, Vertex ID, Vertex Value, Edge Value, Incoming Message,
+  // and Outgoing Message.
+  message VertexScenarioClasses {
+    // The class under test. Must implement 
org.apache.giraph.graph.Computation interface.
+    required string classUnderTest = 1;
+    // The vertex ID class (usually denoted as I). Must implement 
org.apache.hadoop.io.WritableComparable.
+    required string vertexIdClass = 2;
+    // The vertex value class (usually denoted as V). Must implement 
org.apache.hadoop.io.Writable.
+    required string vertexValueClass = 3;
+    // The edge value class (usually denoted as E). Must implement 
org.apache.hadoop.io.Writable.
+    required string edgeValueClass = 4;
+    // The incoming message class (usually denoted as M1). Must implement 
org.apache.hadoop.io.Writable.
+    required string incomingMessageClass = 5;
+    // The outgoing message class (usually denoted as M2). Must implement 
org.apache.hadoop.io.Writable.
+   required string outgoingMessageClass = 6;
+  }
+
+ // VertexContext encapsulates a particular pair of inputs and outputs of 
Computation.compute().
+ message VertexContext {
+   required CommonVertexMasterContext commonContext = 1;
+   required bytes vertexId = 2;
+   required bytes vertexValueBefore = 3;
+   optional bytes vertexValueAfter = 4;
+   // TODO: We might have to break neighbor also to
+   // neighborsBefore and neighborsAfter.
+   repeated Neighbor neighbor = 5;
+   repeated bytes inMessage = 6;
+   repeated OutgoingMessage outMessage = 7;
+
+   // Messages sent by the current vertex.
+   message OutgoingMessage {
+     required bytes destinationId = 1;
+     required bytes msgData = 2;
+   }
+
+   // The outgoing neighbors of the current vertex.
+   message Neighbor {
+     required bytes neighborId = 1;
+     optional bytes edgeValue = 2;
+   }
+ }
+}
+
+
+// GiraphMasterScenario captures the necessary information to
+// replicate the environment under which user's Master.compute()
+// function was called.
+message GiraphMasterScenario {
+  required string masterClassUnderTest = 1;
+  required CommonVertexMasterContext commonContext = 2;
+  optional Exception exception = 3;
+}
+
+// Information related to the exception thrown.
+message Exception {
+  required string message = 1;
+  required string stackTrace = 2;
+}
+
+// Contains common fiels between GiraphVertexScenario.VertexContext
+// and GiraphMasterScenario.
+message CommonVertexMasterContext {
+  required bytes conf = 1;
+  required int64 superstepNo = 2;
+  required int64 totalNumVertices = 3;
+  required int64 totalNumEdges = 4;
+  repeated AggregatedValue previousAggregatedValue = 5;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css
new file mode 100644
index 0000000..cbaf5ad
--- /dev/null
+++ 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css
@@ -0,0 +1,254 @@
+/* 
+ * Style for debugger.
+ */
+
+/* NOTE: This is required because Chrome has a bug that triggers the mouse 
enter
+ * for element at position 0,0 if a select option is selected anywhere on the 
+ * document. Read more here : 
https://code.google.com/p/chromium/issues/detail?id=112455
+ */
+html {
+       margin:1px;
+}
+
+body {
+       background-color:#F5F5F5;
+}
+
+h1 {
+       margin-top: 10px;
+}
+
+svg {
+  background-color: #FFF;
+  cursor: default;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+}
+
+svg:not(.active):not(.ctrl) {
+  cursor: crosshair;
+}
+
+path.link {
+  fill: none;
+  stroke: #000;
+  stroke-width: 4px;
+  cursor: default;
+}
+
+svg:not(.active):not(.ctrl) path.link {
+  cursor: pointer;
+}
+
+path.link.selected {
+  stroke-dasharray: 10,2;
+}
+
+path.link.dragline {
+  pointer-events: none;
+}
+
+path.link.hidden {
+  stroke-width: 0;
+}
+
+circle.node {
+  stroke-width: 1.5px;
+  cursor: pointer;
+}
+
+circle.node.reflexive {
+  stroke: #000 !important;
+  stroke-width: 2.5px;
+}
+
+circle.node.selected {
+  stroke: #000 !important;
+  stroke-width: 3.5px;
+}
+
+text {
+  font: 12px sans-serif;
+  pointer-events: none;
+}
+
+text.tid {
+  text-anchor: middle;
+  font-weight: bold;
+}
+
+.editor {
+       width:100%;
+       height:100%;
+}
+
+#editor-container {
+       overflow: hidden;
+       border-bottom: solid black 1px;
+       border-top: solid black 1px;
+       margin-bottom:20px;
+}
+
+.ui-dialog {
+       moz-box-shadow: 0 0 90px 5px #000;
+       -webkit-box-shadow: 0 0 40px 0px #000;
+}
+
+.ui-widget {
+       font-size:12px;
+}
+
+.ui-widget-overlay {
+       background:#111;
+       opacity:0.6;
+}
+
+.ui-dialog-titlebar {
+       font-family:Arial;
+       font-size:14px;
+}
+
+.ui-widget-header {
+       background:#eee;
+       border:none;
+}
+
+#footer {
+       background-color:#3C0C0C;
+       height:50px;
+       padding:10px;
+       margin-top:20px;
+       color:white;
+}
+
+#attr-view {
+       overflow:hidden;
+}
+
+#attr-view-container {
+       overflow:auto;
+       max-height:500px;
+}
+
+#attr-view th, td {
+       padding:8px;
+       text-align:center;
+       vertical-align:middle;
+}
+
+#node-attr .form-horizontal .control-label  {
+       text-align: left;
+}
+
+#node-attr  {
+       overflow: hidden;
+}
+
+#instructions { 
+       margin-top: 20px;
+}
+
+#controls h1,
+#controls h2, 
+#controls h3, 
+#controls h4 {
+       display: inline-block;
+       vertical-align: middle;
+       margin: auto;
+       margin: 0px 10px 0px 10px;
+}
+
+.editor-controls * {
+       margin-right: 8px;
+}
+
+#controls-container {
+       float:right;
+}
+
+#controls-container .form-inline {
+       clear: both;
+       float:right;
+       margin-bottom:10px;
+}
+
+.valpanel {
+       float: left;
+       height: 100%;
+       border-top: black 1px solid;
+       border-bottom: black 1px solid;
+       border-right: black 2px solid;
+       box-shadow: rgb(136, 136, 136) 5px 0px 15px;
+       background-color:white;
+}
+
+/* Applies to main controls of debugger window
+ * like graph editor and validation panel
+ */
+.debug-control {
+       height: 100%;
+}
+
+
+#debugger-container {
+       height: 504px;
+       margin-bottom: 20px;
+}
+
+#node-attr-flow td {
+       vertical-align: middle;
+}
+
+.editor-aggregators-heading {
+       font: 16px sans-serif;
+}
+
+/* Graph Editor tabular view styles */
+.editor-tablet {
+       width:100%;
+       height:100%;
+       padding: 10px;
+       overflow: auto;
+}
+
+td.tablet-details-control {
+    background: url('../img/details_open.png') no-repeat center center;
+    cursor: pointer;
+}
+tr.shown td.tablet-details-control {
+    background: url('../img/details_close.png') no-repeat center center;
+}
+
+.tablet-data-container {
+       padding: 5px;
+       background-color : #FFDFDF;
+}
+
+.tablet-data-container table {
+       margin:0px;
+}
+
+#code-controls {
+       padding: 5px 0px 5px 0px;
+}
+
+/* Bootstrap overrides */
+.form-control {
+       margin-left:5px;
+}
+
+.nav-tabs li {
+       cursor: pointer;
+}
+
+.slider {
+       width: 100%;
+       margin-left: 15px;
+}
+
+.table th {
+       text-align: center;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
new file mode 100644
index 0000000..fefdd58
--- /dev/null
+++ 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
@@ -0,0 +1,140 @@
+/*!
+ * Slider for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * NOTE: We are using this code to show a slider for changing link distance
+ * between nodes, among other things. 
+ */
+.slider {
+  display: inline-block;
+  vertical-align: middle;
+  position: relative;
+}
+.slider.slider-horizontal {
+  width: 210px;
+  height: 20px;
+}
+.slider.slider-horizontal .slider-track {
+  height: 10px;
+  width: 100%;
+  margin-top: -5px;
+  top: 50%;
+  left: 0;
+}
+.slider.slider-horizontal .slider-selection {
+  height: 100%;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-horizontal .slider-handle {
+  margin-left: -10px;
+  margin-top: -5px;
+}
+.slider.slider-horizontal .slider-handle.triangle {
+  border-width: 0 10px 10px 10px;
+  width: 0;
+  height: 0;
+  border-bottom-color: #0480be;
+  margin-top: 0;
+}
+.slider.slider-vertical {
+  height: 210px;
+  width: 20px;
+}
+.slider.slider-vertical .slider-track {
+  width: 10px;
+  height: 100%;
+  margin-left: -5px;
+  left: 50%;
+  top: 0;
+}
+.slider.slider-vertical .slider-selection {
+  width: 100%;
+  left: 0;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-vertical .slider-handle {
+  margin-left: -5px;
+  margin-top: -10px;
+}
+.slider.slider-vertical .slider-handle.triangle {
+  border-width: 10px 0 10px 10px;
+  width: 1px;
+  height: 1px;
+  border-left-color: #0480be;
+  margin-left: 0;
+}
+.slider input {
+  display: none;
+}
+.slider .tooltip-inner {
+  white-space: nowrap;
+}
+.slider-track {
+  position: absolute;
+  cursor: pointer;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), 
to(#f9f9f9));
+  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+  background-repeat: repeat-x;
+  filter: 
progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', 
endColorstr='#fff9f9f9', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-selection {
+  position: absolute;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), 
to(#f5f5f5));
+  background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
+  background-repeat: repeat-x;
+  filter: 
progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', 
endColorstr='#fff5f5f5', GradientType=0);
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-handle {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  background-color: #0e90d2;
+  background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), 
to(#0480be));
+  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+  background-image: -o-linear-gradient(top, #149bdf, #0480be);
+  background-image: linear-gradient(to bottom, #149bdf, #0480be);
+  background-repeat: repeat-x;
+  filter: 
progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', 
endColorstr='#ff0480be', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px 
rgba(0,0,0,.05);
+  -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px 
rgba(0,0,0,.05);
+  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  opacity: 0.8;
+  border: 0px solid transparent;
+}
+.slider-handle.round {
+  -webkit-border-radius: 20px;
+  -moz-border-radius: 20px;
+  border-radius: 20px;
+}
+.slider-handle.triangle {
+  background: transparent none;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
new file mode 100644
index 0000000..1bd7da8
--- /dev/null
+++ 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
@@ -0,0 +1,72 @@
+.valpanel-icons-container {
+       height: 30px;
+       width: 100%;
+       margin: 5px 10px 5px 0px;
+}
+
+.valpanel-icons-container * {
+       margin: 5px;
+       float:right;
+}
+
+.valpanel-btn-container {
+       margin : 2px 10px 10px 5px;
+       float: left;
+}
+
+.valpanel-btn-container button {
+       border-radius: 0;
+       margin: 5px 10px 5px 5px;
+       text-align: left;
+       width: 100%;
+       max-width: 140px; /* Must match .preview, .expand width */
+}
+
+.valpanel-btn-container.compact {
+       width : 40px;
+}
+
+.valpanel-btn-container button:disabled {
+       background-color: #ebebeb;
+       color:black;
+       opacity: 0.4;
+       border: none;
+}
+
+.valpanel-btn-container button.btn-success {
+       pointer-events:none;
+}
+
+.valpanel-btn-container.preview, .expand {
+       width : 140px;
+}
+
+.valpanel-btn-close {
+       cursor:pointer;
+}
+
+.valpanel-content-container {
+       overflow-y: auto;
+       overflow-x: hidden;
+       padding: 0px 10px 10px 5px;
+       height: 85%;
+       margin-left: 10px;
+}
+
+.valpanel-preloader {
+       background-image: url('../img/preloader.gif');
+       position: absolute;
+       top: 35%;
+       left: 50%;
+       margin: 0 auto;
+       width: 128px;
+       height: 128px;
+}
+
+.valpanel-content-container table th {
+       text-align: center;
+}
+
+.valpanel-content-container table {
+       table-layout: fixed;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html
new file mode 100644
index 0000000..b58ec98
--- /dev/null
+++ 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html
@@ -0,0 +1,319 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Graft</title>
+               <link rel="stylesheet" 
href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css";>
+               <link rel="stylesheet" 
href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css";
 />
+               <link rel="stylesheet" 
href="http://cdn.datatables.net/1.10.0/css/jquery.dataTables.css"; />
+               <link rel="stylesheet" 
href="http://yandex.st/highlightjs/8.0/styles/default.min.css";>
+               <link rel="stylesheet" href="css/valpanel.css">
+               <link rel="stylesheet" href="css/slider/slider.css">
+               <link rel="stylesheet" href="css/app.css">
+               <!--TODO(vikesh) : * Load all JS files asychronously in a 
single script - requirejs. 
+                       * Download external JS files during mvn compile.
+                       * Remove http from src to automatically load based on 
the protocol used to launch this file.
+               -->
+               <script 
src="http://code.jquery.com/jquery-1.11.0.min.js";></script>
+               <script 
src="http://code.jquery.com/jquery-migrate-1.2.1.min.js";></script>
+               <script 
src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js";></script>
+               <script src="http://d3js.org/d3.v3.min.js";></script>
+               <script 
src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js";></script>
+               <script 
src="http://cdn.datatables.net/1.10.0/js/jquery.dataTables.js";></script>
+               <script 
src="http://yandex.st/highlightjs/8.0/highlight.min.js";></script>
+               <script 
src="http://cdn.jsdelivr.net/noty/2.2.2/packaged/jquery.noty.packaged.min.js"; />
+               <script src="js/slider/bootstrap-slider.js"></script>
+               <script src="js/slider/bootstrap-slider.js"></script>
+               <script src="js/utils.js"></script>
+               <script src="js/utils.sampleGraphs.js"></script>
+               <script src="js/editor.core.js"></script>
+               <script src="js/editor.utils.js"></script>
+               <script src="js/valpanel.js"></script>
+               <script src="js/debugger.js"></script>
+
+               <script type="text/javascript">
+                       var giraphDebugger;
+                       var currentSuperstep = -1;
+
+                       $(document).ready(function(){
+                                       var currentCodeUrl = null;
+                                       // Initialize highlighting.
+                                       hljs.initHighlightingOnLoad();
+
+                                       // Performs syntax highlighting for 
dynamically loaded content in pre code.
+                                       function highlightAll() {
+                                               $('pre code').each(function(i, 
e) {hljs.highlightBlock(e)});
+                                       }
+
+                                       /*
+                                        * Sets the currentCode string and the 
code-container div. 
+                                        * @param {object} data - data.code is 
the code content and data.url is the
+                                        * downloadable link of the code 
(text/plain with content-disposition). 
+                                        * NOTE: All code setting should be 
performed through this method to ensure that 
+                                        * currentCodeUrl is set properly.
+                                        */ 
+                                       function setCode(data) {
+                                               currentCodeUrl = data ? 
data.url : null;
+                                               var code = data ? data.code : 
null;
+                                               $('#code-container').html(code);
+                                               
$('#btn-code-download').prop('disabled', code === null);
+                                               highlightAll();
+                                       }
+
+                                       function 
handleTestGraphSuccess(response) {
+                                               setCode(response);
+                                               noty({text : 'Fetched the test 
graph successfully.', type : 'success', timeout: 1000});
+                                       }
+
+                                       function 
handleTestGraphFail(responseText) {
+                                               setCode();
+                                               noty({text : 'Failed to fetch 
the test graph. Message from server : ' + responseText, type : 'error'});
+                                       }
+
+                                       
$("#node-attr-id").keyup(function(event){
+                                               if(event.keyCode===13){
+                                                       
$("#btn-node-attr-save").click();
+                                               }
+                                       });
+                       
+                                       $("#btn-adj-list-get").click(function() 
{
+                                               var adjList = 
Utils.getAdjListStr(editor.getAdjList());
+                                               Utils.downloadFile(adjList, 
'adjList.txt');
+                                       });
+
+                                       // Generate Test Graph - Fetch the test 
graph and display the code.
+                                       
$('#btn-gen-test-graph').click(function() {
+                                               
Utils.fetchTestGraph(giraphDebugger.debuggerServerRoot, 
Utils.getAdjListStrForTestGraph(giraphDebugger.editor.getAdjList()))
+                                               .done(function(response) {
+                                                       
handleTestGraphSuccess(response);
+                                               })
+                                               .fail(function(responseText) {
+                                                       
handleTestGraphFail(responseText);
+                                               });
+                                       });
+
+                                       giraphDebugger = new GiraphDebugger({
+                                                               
debuggerContainer : '#debugger-container',
+                                                               
nodeAttrContainer : '#node-attr-container',
+                                                               
superstepControlsContainer : '#controls-container',
+                                       });
+               
+                                       // Attach events to catpure scenario - 
vertex.
+                                       
giraphDebugger.onCaptureVertex(function(response) {
+                                               setCode(response);
+                                               noty({text : 'Fetched the 
vertex scenario successfully.', type : 'success', timeout: 1000});
+                                       },
+                                       function(response) {
+                                               setCode();
+                                               noty({text : 'Failed to fetch 
the vertex scenario. Message from server : ' + response, type : 'error'});
+                                       });
+
+                                       // Attach events to catpure scenario - 
master.
+                                       
giraphDebugger.onCaptureMaster(function(response) {
+                                               setCode(response);
+                                               noty({text : 'Fetched the 
master scenario successfully.', type : 'success', timeout: 1000});
+                                       },
+                                       function(response) {
+                                               setCode();
+                                               noty({text : 'Failed to fetch 
the master scenario. Message from server : ' + response, type : 'error'});
+                                       });
+
+                                       // Attach events to generate test graph.
+                                       
giraphDebugger.onGenerateTestGraph(handleTestGraphSuccess, handleTestGraphFail);
+                       
+                                       var editor = giraphDebugger.editor;
+                                       // Undirected behaviour.
+                                       $(".graph-type").change(function(event) 
{
+                                                       editor.undirected = 
event.target.value === "undirected" ? true : false;
+                                                       editor.restartGraph();
+                                       });
+
+                                       // Btn attr-view 
+                                       $("#btn-attr-view").click(function() {
+                                               $("#attr-view-content").empty();
+                                               
$("#attr-view-content").append("<tr><th>Node 
ID</th><th>Attributes</th></tr><tr>");
+
+                                               // Attribute view 
+                                               for (var i = 0; i < 
editor.nodes.length; i++) {
+                                                       
$("#attr-view-content").append("<tr></tr>");    
+                                               }
+
+                                               var rows = 
d3.select("#attr-view-content").selectAll("tr")
+                                                       .filter(function(d,i) { 
return i!=0; })
+                                                       .data(editor.nodes);
+
+                                               
rows.append("td").text(function(d){ return d.id; });
+                                               
rows.append("td").append("textarea")
+                                                       .attr("class", 
"form-control") 
+                                                       .attr("rows","1")
+                                                       .text(function(d){ 
return d.attrs.join(); }); 
+
+                                               // show the dialog
+                                               $("#attr-view").dialog({
+                                                               modal:true, 
+                                                               title:"Node 
Attributes View", 
+                                                               
closeText:"close", 
+                                                               maxHeight:600,
+                                                               
closeOnEscape:true,
+                                                               hide : {effect 
: "fade", duration:100}
+                                               });
+
+                                               
$(".ui-widget-overlay").click(function(){ $("#attr-view").dialog("close"); });
+                                       });
+
+                                       
$("#btn-attr-view-save").unbind("click");
+                                       
$("#btn-attr-view-save").click(function(){
+                                                       var rows = 
$("#attr-view-content textarea");
+                                               
+                                                       for(var 
i=0;i<rows.length;i++) {
+                                                               if 
(rows[i]!="") {
+                                                                       
editor.nodes[i].attrs = rows[i].value.split(",");
+                                                               }
+                                                       }
+                                                       editor.restartGraph();
+                                                       
$("#attr-view").dialog("close");
+                                               });
+
+                                       
$("#btn-attr-view-cancel").unbind("click");
+                                       
$("#btn-attr-view-cancel").click(function() {
+                                               $("#attr-view").dialog("close");
+                                       });
+
+                                       // Slider for linkDistance 
+                                       $('#slider-link-distance').slider({
+                                               min : editor.linkDistance,
+                                               value : editor.linkDistance,
+                                               max : 500
+                                       })
+                                       .on('slideStop', function(ev) {
+                                               // resize the linkDistance of 
editor
+                                               editor.linkDistance = ev.value;
+                                               editor.restartGraph();
+                                       });
+
+                                       // Code select handler.
+                                       
$('#btn-code-select').click(function(event) {
+                                               selectText('code-container');   
+                                       });
+
+                                       // Code download handler.
+                                       
$('#btn-code-download').click(function(event) {
+                                               location.href = currentCodeUrl;
+                                       });
+                               });
+               </script>
+       </head>
+  
+       <body>
+               <div id="attr-view" style="display:none">
+                       <div id="attr-view-container">
+                               <table>
+                                       <tbody id="attr-view-content">
+                                       </tbody>
+                               </table>        
+                       </div>
+                       <form role="form">
+                               <div class="form-group">
+                                       <button type="button" class="btn 
btn-primary btn-sm editable-submit" id="btn-attr-view-save"><i class="glyphicon 
glyphicon-ok"></i></button>
+                                       <button type="button" class="btn 
btn-default btn-sm editable-cancel" id="btn-attr-view-cancel"><i 
class="glyphicon glyphicon-remove"></i></button>
+                               </div>
+                       </form>
+               </div>
+
+               <div class="container bs-docs-container">
+                       <h1>Graft <small>GUI</small></h1>
+               </div>
+
+               <div id="debugger-container">
+               </div>
+
+               <!--Container begins-->
+               <div class="container bs-docs-container">
+                       <div class="row"> <!--Row1 begins-->
+                               <!--Column for left side controls-->
+                               <div class="col-md-4">
+                                       <!-- This is a row for the 
directed/undirected controls-->
+                                       <div class="row"> 
+                                               <!--Internal col begins-->
+                                               <div class="col-md-12">         
                                        
+                                                       <!--Form begins-->
+                                                       <form role="form" 
class="form-inline editor-controls"> 
+                                                               <div 
class="form-group">
+                                                                       <label>
+                                                                               
<input type="radio" name="options" id="options_directed" class="graph-type 
form-control" value="directed" checked /> Directed
+                                                                       </label>
+                                                               </div>
+                                                               <div 
class="form-group">
+                                                                       <label>
+                                                                               
<input type="radio" name="options" id="options_undirected" class="graph-type 
form-control" value="undirected" /> Undirected
+                                                                       </label>
+                                                               </div>
+                                                               <!--<button 
type="button" class="btn btn-primary btn-danger form-control btn-sm" 
id="btn-attr-view">Attribute view</button>-->
+                                                       </form> <!--Form ends-->
+                                               </div><!--Internal column 
ends-->
+                                       </div><!--Directed/Undirected Row 
ends-->
+                                       <!-- This is a row for the download 
buttons-->
+                                       <div class="row"> 
+                                       <!--Internal column begins-->
+                                               <div class="col-md-12">         
                                        
+                                                       <button type="button" 
class="btn btn-primary" id="btn-adj-list-get">Download Adjacency List</button>
+                                                       <button type="button" 
class="btn btn-danger" id="btn-gen-test-graph">Generate Test Graph</button>
+                                               </div><!--Internal column 
ends-->
+                                       </div>
+                               </div>
+                               <div class="col-md-8">
+                                       <!--Debugger Superstep controls 
container begin-->
+                                       <div id="controls-container">
+                                       </div>
+                                       <!--Superstep controls end-->
+                               </div>
+                       </div> <!--Row1 ends-->
+
+                       <!-- Row2 begins-->
+                       <div class="row" style="margin-top:20px;">
+                               <div class="col-md-6">
+                                       <!-- Slider for linkDistance-->
+                                       <label>Link Distance : </label> <div 
class="slider" id="slider-link-distance"></div>
+                               </div>
+                       </div> <!--Row2 ends-->
+
+                       <hr />
+                       <div style="margin-top:20px;">
+                               <div id="code-controls">
+                                       <h3>Test Code</h3>
+                                       <button type="button" class="btn 
btn-primary btn-sm" id="btn-code-select">Select All</button>
+                                       <button type="button" class="btn 
btn-danger btn-sm" id="btn-code-download" disabled>Download</button>
+                               </div>
+                               <pre>
+                                       <code id="code-container"></code>
+                               </pre>
+                       </div>
+
+                       <!-- Instructions -->
+                       <div id="instructions">
+                               <hr style="border-top:1px solid black;" />
+                               <p class="lead">
+                                               <mark>Click</mark> in the open 
space to <strong><mark>add a node</mark></strong>, drag from one node to 
another to <strong>add an edge</strong>. <br>
+                                               <mark>Shift-drag</mark> a node 
to <strong><mark>move</mark></strong> the graph layout. <br>
+                                               <mark>Click</mark> a node or an 
edge to <strong><mark>select</mark></strong> it.<br>
+                                               <mark>Double-click</mark> a 
node to open the <strong><mark>node editor</mark></strong>.<br>
+                                               <mark>Click</mark> on the 
Attributes View to open an editable list of <strong><mark>node 
attributes</mark></strong>.<br>
+                                               <mark>Shift-Click</mark> an 
edge to edit <strong>edge value</strong>.
+                               </p>
+                               <p class="lead">
+                                               When a node is selected: 
<strong>R</strong> toggles reflexivity, <strong>Delete</strong> removes the 
node. <br>
+                                               When an edge is selected: 
<strong>L</strong>(eft), <strong>R</strong>(ight), <strong>B</strong>(oth) 
change direction, <strong>Delete</strong> removes the edge.</p>
+                       </div> <!-- Instructions end-->
+               </div><!--Container ends-->
+
+               <!--Footer-->
+               <div id="footer">
+                       <div class="container">
+                               <p class="muted credit">
+                                       Based on <a 
href="http://bl.ocks.org/rkirsling/5001347"; target="_blank">Directed Graph 
Editor</a> by rkirsling. 
+                               <span style="float:right">Code on <a 
href="https://github.com/vikeshkhanna/graph-editor"; 
target="_blank">GitHub</a></span>
+                               </p>
+                       </div>
+               </div><!--Footer-->
+       </body>
+</html>

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
----------------------------------------------------------------------
diff --git 
a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
new file mode 100644
index 0000000..d67f7fc
--- /dev/null
+++ 
b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
@@ -0,0 +1,870 @@
+/*
+ * Abstracts the debugger controls.
+ */
+
+/*
+ * Debugger is a class that encapsulates the graph editor and debugging 
controls.
+ * @param {debuggerContainer} options - Initialize debugger with these options.
+ * @param options.debuggerContainer - Selector for the container of the main 
debugger area. (Editor & Valpanel)
+ * @param options.superstepControlsContainer - Selector for the container of 
the superstep controls.
+ * @constructor
+ */
+function GiraphDebugger(options) {
+    // Initialize (and reset) variables for jobs.
+    this.resetVars();
+    this.debuggerServerRoot = (location.protocol === 'file:' ? 
'http://localhost:8000' : '');
+    this.mode = GiraphDebugger.ModeEnum.EDIT;
+    this.init(options);
+    return this;
+}
+
+/* 
+ * Denotes the mode.
+ * Debug Mode - Editor is in readonly, walking through the supersteps of a 
giraph job.
+ * Edit Mode - Create new graphs in the editor.
+ */
+GiraphDebugger.ModeEnum = {
+    DEBUG : 'debug',
+    EDIT : 'edit'
+}
+
+/*
+ * Initializes the graph editor, node attr modal DOM elements.
+ */
+GiraphDebugger.prototype.init = function(options) {
+    this.editorContainerId = 'editor-container';
+    this.valpanelId = 'valpanel-container';
+
+    // Create divs for valpanel and editor.
+    var valpanelContainer = $('<div />')
+        .attr('class', 'valpanel debug-control')
+        .attr('id', this.valpanelId)
+        .appendTo(options.debuggerContainer);
+
+    var editorContainer = $('<div />')
+        .attr('id', this.editorContainerId)
+        .attr('class', 'debug-control')
+        .appendTo(options.debuggerContainer);
+
+    // Instantiate the editor object.
+    this.editor = new Editor({
+        'container' : '#' + this.editorContainerId,
+        onOpenNode : this.openNodeAttrs.bind(this),
+        onOpenEdge : this.openEdgeVals.bind(this)
+    });
+    
+
+    // Add toggle view event handler.
+    this.editor.onToggleView((function(editorView) {
+        if (editorView === Editor.ViewEnum.TABLET) {
+            this.btnToggleViewSpan.html(' Graph View');
+        } else {
+            this.btnToggleViewSpan.html(' Table View');
+        }
+    }).bind(this));
+
+    // Instantiate the valpanel object.
+    this.valpanel = new ValidationPanel({
+        'container' : '#' + this.valpanelId,
+        'editor' : this.editor,
+        'debuggerServerRoot' : this.debuggerServerRoot,
+        'resizeCallback' : (function() {
+            this.editor.restartGraph();
+        }).bind(this)
+    });
+
+    this.initIds();
+    // Must initialize these members as they are used by subsequent methods.
+    this.superstepControlsContainer = options.superstepControlsContainer;
+    this.initElements(options);
+}
+
+/*
+ * Deferred callbacks for capture scenario
+ */
+GiraphDebugger.prototype.onCaptureVertex = function(done, fail) {
+    this.onCaptureVertex.done = this.valpanel.onCaptureVertex.done = done;
+    this.onCaptureVertex.fail = this.valpanel.onCaptureVertex.fail = fail;
+}
+
+/*
+ * Deferred callbacks for generate test graph.
+ */
+GiraphDebugger.prototype.onGenerateTestGraph = function(done, fail) {
+    this.onGenerateTestGraph.done = done;
+    this.onGenerateTestGraph.fail = fail;
+}
+
+/*
+ * Deferred callbacks for capture scenario
+ */
+GiraphDebugger.prototype.onCaptureMaster = function(done, fail) {
+    this.onCaptureMaster.done = done;
+    this.onCaptureMaster.fail = fail;
+}
+
+/*
+ * Reset job-related vars to the initial state. 
+ */
+GiraphDebugger.prototype.resetVars = function() { 
+    // Node that is currently double clicked.
+    this.selectedNodeId = null;
+    // Initialize current superstep to -2 (Not in debug mode)
+    this.currentSuperstepNumber = -2;
+    // ID of the job currently being debugged.
+    this.currentJobId = null;
+    // Minimum value of superstepNumber
+    this.minSuperstepNumber = -1;
+    // Maximum value of superstepNumber - Depends on the job.
+    // TODO(vikesh): Fetch from debugger server in some AJAX call. Replace 
constant below.
+    this.maxSuperstepNumber = 15;
+    // Caches the scenarios to show correct information when going backwards.
+    // Cumulatively builds the state of the graph starting from the first 
superstep by merging
+    // scenarios on top of each other.
+    this.stateCache = {"-1": {}};
+}
+
+/*
+ * Initialize DOM element Id constants
+ */
+GiraphDebugger.prototype.initIds = function() {
+    this.ids = {
+        // IDs of elements in node attribute modal.
+        _nodeAttrModal : 'node-attr',
+        _nodeAttrId : 'node-attr-id',
+        _nodeAttrAttrs : 'node-attr-attrs',
+        _nodeAttrGroupError : 'node-attr-group-error',
+        _nodeAttrError : 'node-attr-error',
+        _btnNodeAttrSave : 'btn-node-attr-save',
+        _btnNodeAttrCancel : 'btn-node-attr-cancel',
+        // IDs of elements in edge values modal
+        _edgeValModal : 'edge-vals',
+        // IDs of elements in Edit Mode controls.
+        _selectSampleGraphs : 'sel-sample-graphs',
+        // IDs of elements in Superstep controls.
+        _btnPrevStep : 'btn-prev-step',
+        _btnNextStep : 'btn-next-step',
+        _btnEditMode : 'btn-edit-mode',
+        _btnFetchJob : 'btn-fetch-job',
+        _btnCaptureVertexScenario : 'btn-capture-scenario',
+        _btnCaptureMasterScenario : 'btn-capture-scenario',
+        _btnToggleView : 'btn-toggle-view'
+    };
+}
+
+/*
+ * Initializes the input elements inside the node attribute modal form.
+ * @param nodeAttrForm - Form DOM object.
+ */
+GiraphDebugger.prototype.initInputElements = function(nodeAttrForm) {
+   // Create form group for ID label and text box.
+    var formGroup1 = $('<div />')
+        .addClass('form-group')
+        .appendTo(nodeAttrForm);
+
+    // Create node ID label.
+    var nodeAttrIdLabel = $('<label />')
+        .attr('for', this.ids._nodeAttrId)
+        .addClass('control-label col-sm-4')
+        .html('Node ID:')
+        .appendTo(formGroup1);
+
+    // Create the ID input textbox.
+    // Add it to a column div, which in turn is added to formgroup2.
+    this.nodeAttrIdInput = $('<input>')
+        .attr('type', 'text')
+        .attr('id', this.ids._nodeAttrId)
+        .addClass('form-control')
+        .appendTo($('<div>').addClass('col-sm-8').appendTo(formGroup1));
+
+    // Create the form group for attributes label and input.
+    var formGroup2 = $('<div />')
+        .addClass('form-group')
+        .appendTo(nodeAttrForm);
+
+    var nodeAttrAttributeLabel = $('<label />')
+        .attr('for', this.ids._nodeAttrAttrs)
+        .addClass('control-label col-sm-4')
+        .html('Attributes: ')
+        .appendTo(formGroup2);
+
+    // Create the Attributes input textbox.
+    // Add it to a column div, which in turn is added to formgroup2.
+    this.nodeAttrAttrsInput = $('<input>')
+        .attr('type', 'text')
+        .attr('id', this._nodeAttrAttrs)
+        .addClass('form-control')
+        .appendTo($('<div>').addClass('col-sm-8').appendTo(formGroup2));
+
+    // Create form group for buttons.
+    var formGroupButtons = $('<div />')
+        .addClass('form-group')
+        .appendTo(nodeAttrForm);
+
+    var buttonsContainer = $('<div />')
+        .addClass('col-sm-12')
+        .appendTo(formGroupButtons);
+
+    this.btnNodeAttrSubmit = Utils.getBtnSubmitSm()
+        .attr('type', 'submit')
+        .attr('id', this.ids._btnNodeAttrSave)
+        .appendTo(buttonsContainer);
+
+    this.btnNodeAttrCancel = Utils.getBtnCancelSm()
+        .attr('id', this.ids._btnNodeAttrCancel)
+        .appendTo(buttonsContainer);
+
+    this.nodeAttrGroupError = $('<div />')
+        .addClass('form-group has-error')
+        .attr('id', this.ids._nodeAttrGroupError)
+        .hide()
+        .appendTo(nodeAttrForm);
+
+    var errorLabel = $('<label />')
+        .addClass('control-label')
+        .attr('id', this.ids._nodeAttrError)
+        .html('Node ID must be unique')
+        .appendTo($('<div 
class="col-sm-12"></div>').appendTo(this.nodeAttrGroupError));
+}
+
+/*
+ * Initializes the message container and all elements within it.
+ * Returns the message container DOM object.
+ * @param nodeAttrForm - Form DOM object.
+ */
+GiraphDebugger.prototype.initMessageElements = function(nodeAttrForm) {
+    var messageContainer = $('<div />')
+        .appendTo(nodeAttrForm)
+
+    var messageTabs = $('<ul />')
+        .addClass('nav nav-tabs')
+        .html('<li class="active"><a id="node-attr-received" class="nav-msg" 
href="#!">Received</a></li>' + 
+            '<li><a id="node-attr-sent" class="nav-msg" 
href="#!">Sent</a></li>' +
+            '<li><a id="node-attr-edgevals" class="nav-msg" href="#!">Edge 
Values</a></li>')
+        .appendTo(messageContainer);
+
+    var tableContainer = $('<div />')
+        .addClass('highlight')
+        .appendTo(messageContainer);
+
+    this.flowTable = $('<table />')
+        .addClass('table')
+        .attr('id', 'node-attr-flow')
+        .appendTo(messageContainer);
+}
+
+/*
+ * Initializes Superstep controls.
+ * @param superstepControlsContainer - Selector for the superstep controls 
container.
+ */
+GiraphDebugger.prototype.initSuperstepControls = 
function(superstepControlsContainer) {
+    /*** Edit Mode controls ***/
+    // Create the div with controls visible in Edit Mode
+    this.editModeGroup = $('<div />')
+        .appendTo(superstepControlsContainer);
+
+    // Create the form that fetches the superstep data from debugger server.
+    var formFetchJob = $('<div />')
+        .attr('class', 'form-inline')
+        .appendTo(this.editModeGroup);
+
+    // Fetch job details for job id textbox.
+    this.fetchJobIdInput = $('<input>')
+        .attr('type', 'text')
+        .attr('class', 'form-control ')
+        .attr('placeholder', 'Job ID')
+        .appendTo(formFetchJob);
+
+    this.btnFetchJob = $('<button />')
+        .attr('id', this.ids._btnFetchJob)
+        .attr('type', 'button')
+        .attr('class', 'btn btn-danger form-control')
+        .html('Fetch')
+        .appendTo(formFetchJob);
+
+    // Create the control for creating sample graphs.
+    var formSampleGraphs = $('<div />')
+        .attr('class', 'form-inline')
+        .appendTo(this.editModeGroup);
+
+    this.selectSampleGraphs = $('<select />')
+        .attr('class', 'form-control')
+        .attr('id', this.ids._selectSampleGraphs)
+        .appendTo(formSampleGraphs);
+
+    // Add the graph names to the select drop down.
+    $.each(Utils.sampleGraphs, (function (key, value) {
+        $(this.selectSampleGraphs).append($('<option />').attr('value', 
key).html(key));
+    }).bind(this));
+
+    this.sampleGraphsInput = $('<input />')
+        .attr('class', 'form-control')
+        .attr('placeholder', '# of vertices')
+        .appendTo(formSampleGraphs);
+
+    this.btnSampleGraph = $('<button />')
+        .attr('class', 'btn btn-primary form-control')
+        .html('Generate')
+        .appendTo(formSampleGraphs);
+
+    /*** DEBUG MODE controls ***/
+    this.debugModeGroup = $('<div />')
+        .appendTo(superstepControlsContainer)
+        .hide();
+
+    // Initialize the actual controls.
+    var formControls = $('<div />')
+        .attr('id', 'controls')
+        .attr('class', 'form-inline')
+        .appendTo(this.debugModeGroup);
+
+    this.btnPrevStep = $('<button />')
+        .attr('class', 'btn btn-default bt-step form-control')
+        .attr('id', this.ids._btnPrevStep)
+        .attr('disabled', 'true')
+        .append(
+            $('<span />')
+            .attr('class', 'glyphicon glyphicon-chevron-left')
+            .html(' Previous')
+        )
+        .appendTo(formControls);
+
+    var superstepLabel = $('<h2><span id="superstep">-1</span>' +
+        '<small> Superstep</small></h2>')
+        .appendTo(formControls);
+
+    // Set this.superstepLabel to the actual label that will be updated.
+    this.superstepLabel = $('#superstep');
+
+    this.btnNextStep = $('<button />')
+        .attr('class', 'btn btn-default btn-step form-control')
+        .attr('id', this.ids._btnNextStep)
+        .append(
+            $('<span />')
+            .attr('class', 'glyphicon glyphicon-chevron-right')
+            .html(' Next')
+        )
+        .appendTo(formControls);
+
+    // Return to the edit mode - Exiting the debug mode.
+    this.btnEditMode = $('<button />')
+        .attr('class', 'btn btn-default btn-step form-control')
+        .attr('id', this.ids._btnEditMode)
+        .append(
+            $('<span />')
+            .attr('class', 'glyphicon glyphicon-pencil')
+            .html(' Edit Mode')
+        )
+        .appendTo(formControls);
+
+   // Change the text value of this span when toggling views.
+   this.btnToggleViewSpan = $('<span />')
+                .attr('class', 'glyphicon glyphicon-cog')
+                .html(' Table View');
+
+   // Toggle the editor between the table and graph view.
+   this.btnToggleView = $('<button />')
+        .attr('class', 'btn btn-default btn-step form-control')
+        .attr('id', this.ids._btnToggleView)
+        .append(this.btnToggleViewSpan)
+        .appendTo(formControls);
+
+    // Capture Scenario group
+    var captureScenarioForm = $('<div />')
+        .attr('class', 'form-inline')
+        .appendTo(this.debugModeGroup);
+    
+    // Input text box to input the vertexId
+    this.captureVertexIdInput = $('<input>')
+        .attr('type', 'text')
+        .attr('class', 'form-control ')
+        .attr('placeholder', 'Vertex ID')
+        .appendTo(captureScenarioForm);
+
+    // Capture Vertex Scenario Scenario button.
+    this.btnCaptureVertexScenario = $('<button>')
+        .attr('type', 'button')
+        .attr('id', this.ids._btnCaptureVertexScenario)
+        .attr('class', 'btn btn-primary form-control')
+        .html('Capture Vertex')
+        .appendTo(captureScenarioForm);
+
+    // Capture Master
+    this.btnCaptureMasterScenario = $('<button>')
+        .attr('type', 'button')
+        .attr('id', this.ids._btnCaptureMasterScenario)
+        .attr('class', 'btn btn-danger form-control')
+        .html('Capture Master')
+        .appendTo(captureScenarioForm);
+
+    // Initialize handlers for events
+    this.initSuperstepControlEvents();
+}
+
+/*
+ * Initializes the handlers of the elements on superstep controls.
+ */
+GiraphDebugger.prototype.initSuperstepControlEvents = function() {
+    // On clicking Fetch button, send a request to the debugger server
+    // Fetch the scenario for this job for superstep -1
+    $(this.btnFetchJob).click((function(event) {
+        this.currentJobId = $(this.fetchJobIdInput).val();
+        this.currentSuperstepNumber = 0;
+        this.changeSuperstep(this.currentJobId, this.currentSuperstepNumber);
+        this.toggleMode();
+    }).bind(this));
+    // On clicking the edit mode button, hide the superstep controls and show 
fetch form.
+    $(this.btnEditMode).click((function(event) {
+        this.toggleMode();
+    }).bind(this));
+
+    // Handle the next and previous buttons on the superstep controls.
+    $(this.btnNextStep).click((function(event) {
+        this.currentSuperstepNumber += 1;
+        this.changeSuperstep(this.currentJobId, this.currentSuperstepNumber);
+    }).bind(this));
+
+    $(this.btnPrevStep).click((function(event) {
+        this.currentSuperstepNumber -= 1;
+        this.changeSuperstep(this.currentJobId, this.currentSuperstepNumber);
+    }).bind(this));
+
+    // Handle the capture scenario button the superstep controls.
+    $(this.btnCaptureVertexScenario).click((function(event){
+        // Get the deferred object.
+        var vertexId = $(this.captureVertexIdInput).val();
+        Utils.fetchVertexTest(this.debuggerServerRoot, this.currentJobId, 
+            this.currentSuperstepNumber, vertexId, 'reg')
+        .done((function(response) {
+            this.onCaptureVertex.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureVertex.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+    // Handle the master capture scenario button the superstep controls.
+    $(this.btnCaptureMasterScenario).click((function(event){
+        Utils.fetchMasterTest(this.debuggerServerRoot, this.currentJobId, 
this.currentSuperstepNumber)
+        .done((function(response) {
+            this.onCaptureMaster.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureMaster.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+
+    // Handle the toggle view button.
+    $(this.btnToggleView).click((function(event) {
+        this.editor.toggleView();
+    }).bind(this));
+
+    // Handle the generate sample graph button.
+    $(this.btnSampleGraph).click((function(event) { 
+        var numVertices = $(this.sampleGraphsInput).val();
+        var graphTypeKey = $(this.selectSampleGraphs).val();
+        
this.editor.buildGraphFromSimpleAdjList(Utils.sampleGraphs[graphTypeKey](numVertices));
+
+        Utils.fetchTestGraph(this.debuggerServerRoot, 
Utils.getAdjListStrForTestGraph(this.editor.getAdjList()))
+        .done((function(response) {
+            this.onGenerateTestGraph.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onGenerateTestGraph.fail(response.responseText);
+        }).bind(this));
+    }).bind(this));
+}
+
+/*
+ * Fetches the data for this superstep, updates the superstep label, graph 
editor
+ * and disables/enables the prev/next buttons.
+ * @param {int} superstepNumber : Superstep to fetch the data for.
+ */
+GiraphDebugger.prototype.changeSuperstep = function(jobId, superstepNumber) {
+    console.log("Changing Superstep to : " + superstepNumber);
+    $(this.superstepLabel).html(superstepNumber);
+    // Update data of the valpanel
+    this.valpanel.setData(jobId, superstepNumber);
+
+    // Fetch the max number of supersteps again. (Online case)
+    $.ajax({
+            url : this.debuggerServerRoot + "/supersteps",
+            data : {'jobId' : this.currentJobId}
+    })
+    
+    .done((function(response) {
+        this.maxSuperstepNumber = Math.max.apply(Math, response);
+    }).bind(this))
+    .fail(function(response) {
+    });
+
+    // If scenario is already cached, don't fetch again.
+    if (superstepNumber in this.stateCache) {
+        this.modifyEditorOnScenario(this.stateCache[superstepNumber]);
+    } else {
+        // Show preloader while AJAX request is in progress.
+        this.editor.showPreloader();
+        // Fetch from the debugger server.
+        $.ajax({
+            url : this.debuggerServerRoot + '/scenario',
+            dataType : 'json',
+            data: { 'jobId' : jobId, 'superstepId' : superstepNumber }
+        })
+        .retry({
+            times : 5, 
+            timeout : 2000,
+            retryCallback : function(remainingTimes) {
+                // Failed intermediately. Will be retried. 
+                noty({text : 'Failed to fetch job. Retrying ' + remainingTimes 
+ ' more times...', type : 'warning', timeout : 1000});
+            }
+        })
+        .done((function(data) {
+            console.log(data);
+            // Add data to the state cache. 
+            // This method will only be called if this superstepNumber was not
+            // in the cache already. This method just overwrites without check.
+            // If this is the first time the graph is being generated, (count 
= 1)
+            // start from scratch - build from adjList.
+            if (Utils.count(this.stateCache) === 1) {
+                this.stateCache[superstepNumber] = $.extend({}, data);
+                this.editor.buildGraphFromAdjList(data);
+            } else {
+                // Merge this data onto superstepNumber - 1's data 
+                this.stateCache[superstepNumber] = 
this.mergeStates(this.stateCache[superstepNumber - 1], data);
+                this.modifyEditorOnScenario(this.stateCache[superstepNumber]);
+            }
+        }).bind(this))
+        .fail(function(error) {
+            noty({text : 'Failed to fetch job. Please check your network and 
debugger server.', type : 'error'});
+        })
+        .always((function() {
+            // Hide Editor's preloader.
+            this.editor.hidePreloader();
+        }).bind(this));
+    }
+    // Superstep changed. Enable/Disable the prev/next buttons.
+    $(this.btnNextStep).attr('disabled', superstepNumber === 
this.maxSuperstepNumber);
+    $(this.btnPrevStep).attr('disabled', superstepNumber === 
this.minSuperstepNumber);
+}
+
+
+/*
+ * Modifies the editor for a given scenario.
+ */
+GiraphDebugger.prototype.modifyEditorOnScenario = function(scenario) {
+    console.log(scenario); 
+    // Add new nodes/links received in this scenario to graph.
+    this.editor.addToGraph(scenario);
+    // Disable the nodes that were not traced as part of this scenario.
+    for (var i = 0; i < this.editor.nodes.length; i++) {
+        var nodeId = this.editor.nodes[i].id;
+        if ((nodeId in scenario) && scenario[nodeId].debugged != false) {
+            this.editor.nodes[i].enabled = true;
+        } else {
+            this.editor.nodes[i].enabled = false;
+        }
+    }
+    // Update graph data with this scenario.
+    this.editor.updateGraphData(scenario);
+}
+
+/*
+ * Creates the document elements, like Node Attributes modal.
+ */
+GiraphDebugger.prototype.initElements = function() {
+    // Div for the node attribute modal.
+    this.nodeAttrModal = $('<div />')
+        .attr('class', 'modal')
+        .attr('id', this.ids._nodeAttrModal)
+        .hide()
+
+   // Div for edge values modal.
+   this.edgeValModal = $('<div />')
+       .attr('class', 'modal')
+       .attr('id', this.ids._edgeValModal)
+       .hide()
+
+   this.edgeValForm = $('<form />')
+       .addClass('form-horizontal')
+       .appendTo(this.edgeValModal);
+       
+    // Create a form and append to nodeAttr
+    var nodeAttrForm = $('<form />')
+        .addClass('form-horizontal')
+        .appendTo(this.nodeAttrModal);
+
+    this.initInputElements(nodeAttrForm);
+    this.initMessageElements(nodeAttrForm);
+    this.initSuperstepControls(this.superstepControlsContainer);
+
+    // Initialize the node attr modal dialong.
+    $(this.nodeAttrModal).dialog({
+        modal : true,
+        autoOpen : false,
+        width : 300,
+        resizable : false,
+        closeOnEscape : true,
+        hide : {effect : 'fade', duration : 100},
+        close : (function() {
+            this.selectedNodeId = null;
+        }).bind(this)
+    });
+
+    // Initialize the edge values modal dialog.
+    $(this.edgeValModal).dialog({
+        modal : true,
+        autoOpen : false,
+        width : 250,
+        resizable : false,
+        title : 'Edge',
+        closeOnEscape : true,
+        hide : {effect : 'fade', duration : 100},
+        close : (function() {
+            this.selectedLink = null;
+        }).bind(this)
+    });
+
+    // Attach events.
+    // Click event of the Sent/Received tab buttons
+    $('.nav-msg').click((function(event) {
+        // Render the table
+        var clickedId = event.target.id;
+        this.toggleMessageTabs(clickedId);
+        if (clickedId === 'node-attr-sent') {
+            var messageData = 
this.editor.getMessagesSentByNode(this.selectedNodeId);
+            this.showMessages(messageData);
+        } else if(clickedId === 'node-attr-received') {
+            var messageData = 
this.editor.getMessagesReceivedByNode(this.selectedNodeId);
+            this.showMessages(messageData);
+        } else {
+            this.showEdgeValues(this.selectedNodeId, this.selectedEdgeValues);
+        }
+    }).bind(this));
+    // Attach mouseenter event for valpanel - Preview (Expand to the right)
+    $(this.valpanel.container).mouseenter((function(event) {
+        if (this.valpanel.state === ValidationPanel.StateEnum.COMPACT) {
+            this.valpanel.preview();
+        }
+    }).bind(this));
+    // Attach mouseleave event for valpanel - Compact (Compact to the left)
+    $(this.valpanel.container).mouseleave((function(event) {
+        // The user must click the close button to compact from the expanded 
mode.
+        if (this.valpanel.state != ValidationPanel.StateEnum.EXPAND) {
+            this.valpanel.compact();
+        }
+    }).bind(this));
+}
+
+/* 
+ * Handler for opening edge values. 
+ * Opens the edge value modal to allow editing/viewing edge values.
+ */
+GiraphDebugger.prototype.openEdgeVals = function(data) {
+    // Set the currently opened link.
+    this.selectedLink = data.link;
+    $(this.edgeValModal).dialog('option', 'position', [data.event.clientX, 
data.event.clientY]);
+    $(this.edgeValForm).empty();
+    // Data for the form.
+    var table = $('<table />').addClass('table').appendTo(this.edgeValForm);
+    var edges = this.editor.getEdges(data.link);
+    // Convert edges array to a map to be able to cache onChange results.
+    edgeValuesCache = {};
+    $.each(edges, function(i, edge) {
+        edgeValuesCache[edge.source.id] = edge;
+    });
+
+    $.each(edgeValuesCache, (function(sourceId, edge) {
+        var tr = document.createElement('tr');
+        var edgeElement = edge.edgeValue ? edge.edgeValue : 'undefined';
+        if (!this.editor.readonly) {
+            edgeElement = $('<input type="text" />')
+                .attr('value', edge.edgeValue)
+                .css('width', '100%')
+                .attr('placeholder', edge.edgeValue)
+                .change(function(event) {
+                    // Save the temporarily edited values to show them as such
+                    // when this tab is opened again.
+                    edgeValuesCache[sourceId].edgeValue = event.target.value;
+                });
+        }
+        $(tr).append($('<td />').html("{0}->{1}".format(edge.source.id, 
edge.target.id)));
+        $(tr).append($('<td />').append(edgeElement));
+        table.append(tr);
+    }).bind(this));
+
+    Utils.getBtnSubmitSm()
+        .attr('type', 'submit')
+        .appendTo(this.edgeValForm)
+        .click((function(event) {
+            event.preventDefault();
+            // Save the temporary cache back to the editor object.
+            $.each(edgeValuesCache, (function(sourceId, edge) {
+                this.editor.addEdge(sourceId, edge.target.id, edge.edgeValue);
+            }).bind(this));
+            $(this.edgeValModal).dialog('close');
+            this.editor.restart();
+        }).bind(this));
+
+    Utils.getBtnCancelSm()
+        .appendTo(this.edgeValForm)
+        .click((function() {
+            $(this.edgeValModal).dialog('close');
+        }).bind(this));
+    $(this.edgeValModal).dialog('open');
+    // setTimeout is required because of a Chrome bug - jquery.focus doesn't 
work expectedly.
+    setTimeout((function() { $(this.edgeValModal).find('form 
input:text').first().focus(); }).bind(this), 1);
+    $('.ui-widget-overlay').click((function() { 
$(Utils.getSelectorForId(this.ids._edgeValModal)).dialog('close'); 
}).bind(this));
+}
+
+/*
+ * This is a double-click handler.
+ * Called from the editor when a node is double clicked.
+ * Opens the node attribute modal with NodeId, Attributes, Messages and Edge 
Values.
+ */
+GiraphDebugger.prototype.openNodeAttrs = function(data) {
+    // Set the currently double clicked node
+    this.selectedNodeId = data.node.id;
+    // Store the current edge values for this node in a temporary map.
+    // This is used by the Edge Values tab.
+    this.selectedEdgeValues = 
this.editor.getEdgeValuesForNode(this.selectedNodeId);
+
+    $(this.nodeAttrIdInput).attr('value', data.node.id)
+        .attr('placeholder', data.node.id);
+    $(this.nodeAttrAttrsInput).attr('value', data.node.attrs);
+    $(this.nodeAttrGroupError).hide();
+    $(this.nodeAttrModal).dialog('option', 'position', [data.event.clientX, 
data.event.clientY]);
+    $(this.nodeAttrModal).dialog('option', 'title', 'Node (ID: ' + 
data.node.id + ')');
+    $(this.nodeAttrModal).dialog('open');
+    // Set the focus on the Attributes input field by default.
+    $(this.nodeAttrModal).find('form input').eq(1).focus();
+    $('.ui-widget-overlay').click((function() { 
$(Utils.getSelectorForId(this.ids._nodeAttrModal)).dialog('close'); 
}).bind(this));
+
+    $(this.btnNodeAttrCancel).click((function() {
+        $(this.nodeAttrModal).dialog('close');
+    }).bind(this));
+
+    $(this.btnNodeAttrSubmit).unbind('click');
+    $(this.btnNodeAttrSubmit).click((function(event) {
+        event.preventDefault();
+        var new_id = $(this.nodeAttrIdInput).val();
+        var new_attrs = $(this.nodeAttrAttrsInput).val();
+        // Check if this id is already taken.
+        if (data.editor.getNodeIndex(new_id) >= 0 && new_id != data.node.id) {
+            $(this.nodeAttrGroupError).show();
+            return;
+        }
+        data.node.id = new_id;
+        data.node.attrs = new_attrs;
+        // Save the stored edge values. If not edited by the user, overwritten 
by the original values).
+        $.each(this.selectedEdgeValues, (function(targetId, edge) {
+            // This method is safe - If an edge exists, only overwrites the 
edge value.
+            data.editor.addEdge(this.selectedNodeId, targetId, edge.edgeValue);
+        }).bind(this));
+        data.editor.restart();
+        $(this.nodeAttrModal).dialog('close');
+    }).bind(this));
+
+    // Set the 'Received' tab as the active tab and show messages.
+    this.toggleMessageTabs('node-attr-received');
+    
this.showMessages(data.editor.getMessagesReceivedByNode(this.selectedNodeId));
+}
+
+/*
+ * Makes the clicked message tab active and the other inactive,
+ * by setting/removing the 'active' classes on the corresponding elements.
+ * @param - Suffix of the clicked element (one of 'sent'/'received')
+ */
+GiraphDebugger.prototype.toggleMessageTabs = function(clickedId) {
+    if (this.currentlyActiveTab) {
+        $(this.currentlyActiveTab).parent().removeClass('active');
+    }
+    this.currentlyActiveTab = $('#' + clickedId);
+    $(this.currentlyActiveTab).parent().addClass('active');
+}
+
+/*
+ * Populates the messages table on the node attr modal with the message data
+ * @param messageData - The data of the sent/received messages from/to this 
node.
+ */
+GiraphDebugger.prototype.showMessages = function(messageData) {
+    this.flowTable.html('');
+    for (var nodeId in messageData) {
+        var tr = document.createElement('tr');
+        $(tr).html('<td>' + nodeId + '</td><td>' +
+            messageData[nodeId] + '</td>');
+        this.flowTable.append(tr);
+    }
+}
+
+/*
+ * Populates the edge value table on the node attr modal with the edge vaue 
data.
+ * Uses this.selectedEdgeValues and this.selectedNodeId - must be populated 
before calling this method.
+ * Format this.selectedEdgeValues : { targetNodeId : edgeValue }
+ */
+GiraphDebugger.prototype.showEdgeValues = function() {
+    this.flowTable.html('');
+    $.each(this.selectedEdgeValues, (function(nodeId, edge) {
+        var tr = document.createElement('tr');
+        var edgeElement = edge.edgeValue;
+        if (!this.editor.readonly) {
+            edgeElement = $('<input type="text" />')
+                .attr('value', edge.edgeValue)
+                .attr('placeholder', edge.edgeValue)
+                .change((function(event) {
+                    // Save the temporarily edited values to show them as such
+                    // when this tab is opened again.
+                    this.selectedEdgeValues[nodeId].edgeValue = 
event.target.value;
+                }).bind(this));
+        }
+        $(tr).append($('<td />').html(nodeId));
+        $(tr).append($('<td />').append(edgeElement));
+        this.flowTable.append(tr);
+    }).bind(this));
+}
+
+/*
+ * Merges deltaState on baseState. Merge implies ->
+ * Keep all the values of baseState but overwrite if deltaState
+ * has them too. If deltaState has some vertices not in baseState, add them.
+ */
+GiraphDebugger.prototype.mergeStates = function(baseState, deltaState) {
+    var newState = $.extend(true, {}, baseState);
+    // Start with marking all nodes in baseState as not debugged.
+    // Only nodes debugged in deltaState will be marked as debugged.
+    for (nodeId in baseState) {
+        newState[nodeId].debugged = false;    
+    }
+    for (nodeId in deltaState) {
+        // Add this node's properties from deltaState
+        newState[nodeId] = $.extend({}, deltaState[nodeId]);
+        // If nodeId was in deltaState, mark as debugged.
+        newState[nodeId].debugged = true;
+    }
+    return newState;
+}
+
+/*
+ * Toggles between the debug and edit modes.
+ */
+GiraphDebugger.prototype.toggleMode = function() {
+    if (this.mode === GiraphDebugger.ModeEnum.DEBUG) {
+        this.mode = GiraphDebugger.ModeEnum.EDIT;
+        if (this.editor.view != Editor.ViewEnum.GRAPH) {
+            this.editor.toggleView();
+        }
+        // Start with a sample graph as usual.
+        this.editor.setReadonly(false);
+        this.editor.buildSample();
+        // Show Fetch Job and hide controls
+        $(this.debugModeGroup).hide();
+        $(this.editModeGroup).show();
+        // Reset vars
+        this.resetVars();
+    } else {
+        this.mode = GiraphDebugger.ModeEnum.DEBUG;
+        // Set the editor in readonly mode.
+        this.editor.setReadonly(true);
+        // Show Form controls and hide fetch job.
+        $(this.debugModeGroup).show();
+        $(this.editModeGroup).hide();
+    }
+}

Reply via email to