text: /**
   * 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.
   */
  
  LogGraph.ServerGraph = function (asyncq, canvas, starttime, endtime, filter) {
      this.starttime = starttime;
      this.endtime = endtime;
      this.millis = endtime - starttime;
      this.nextserverid = 0;
      this.serveroffset = 100;
      this.filter = filter;
  
      this.pixels_per_tick = 20;
      this.ticker = new LogGraph.ticker();
  
  
      var paper = Raphael(canvas, 1, 1);
  
      var self = this;
  
      this.timescale = new LogGraph.timescale(starttime, endtime);
      this.objects = new Array();
  
      this.add = function (obj) {
          this.objects.push(obj);
      }
  
      this.tick_to_x = function (timestamp) {
          var x = timestamp * this.pixels_per_tick;
          return x;
      };
  
      this._drawTime = function (paper, x, time) {
          var p = paper.path("M" + x + " 0 L" + x + " " + paper.height);
          var t = paper.text(x, 10, dateFormat(time, "h:MM:ss,l"));
  
          t.hide();
          p.mouseover(function (evt) {
              t.show();
              p.attr({stroke: "red"});
          });
          p.mouseout(function (evt) {
              t.hide();
              p.attr({stroke: "lightgray"});
          });
  
          return p;
      };
  
      this.draw = function (paper) {
          var grid = paper.set();
          for (var i = 0; i < paper.height; i += 20) {
              grid.push(paper.path("M0 " + i + " L" + paper.width + " " + i));
          }
          var lasttick = this.starttime;
          var scale = 500; // 500 ms
  
          var y = 0;
  
          for (var t = 0, len = this.ticker.ticks.length; t < len; t++) {
              var basex = t * this.pixels_per_tick;
              var thistick = this.ticker.ticks[t];
              var nexttick = t + 1 == this.ticker.ticks.length ? this.endtime : this.ticker.ticks[t + 1];
              if (nexttick == thistick) {
                  continue;
              }
              var time = thistick - lasttick;
              var first = scale - (lasttick % scale);
  
              /*		for (var i = 0; (first+scale*i) < time; i++) {
                  
                  var toffset = first+scale*i;
                  var x = basex + LogGraph._pixels_per_tick * toffset/time;
                  grid.push(this._drawTime(paper, x, lasttick + toffset, grid));
                  
                  }*/
  
  
              //grid.push(paper.path("M" + i + " 0 L" + i + " " + paper.height));
              lasttick = thistick;
          }
          grid.attr({stroke: "lightgray"});
          this.timescale.draw(paper);
  
          for (o in this.objects) {
              this.objects[o].draw(paper);
          }
      };
  
  
      var processdata = function (data) {
          var servermap = {};
          var servers = data.servers;
          var count = 0;
          for (s in servers) {
              var server = new LogGraph.ServerGraph.server(self, "Server " + servers[s]);
              servermap[servers[s]] = server;
              self.add(server);
              count++;
          }
  
          var messages = {};
          var events = data.events;
          for (var i in events) {
              var e = events[i];
              var t = e.time;
              if (e.type == "stateChange") {
                  servermap[e.server].addState(e.state, self.ticker.tick(e.time));
              }
              if (e.type == "postmessage") {
                  src = servermap[e.src];
                  dst = servermap[e.dst];
                  var key = "key:s" + e.src + ",d" + e.dst + ",z" + e.zxid;
  
                  var m = new LogGraph.ServerGraph.message(self, src, self.ticker.tick(e.time), dst, e.zxid);
                  messages[key] = m;
              }
              if (e.type == "delivermessage") {
                  var key = "key:s" + e.src + ",d" + e.dst + ",z" + e.zxid;
  
                  var m = messages[key];
                  if (m) {
                      m.dsttime = self.ticker.tick(e.time);
                      m.name = "Propose";
                      self.add(m);
                      delete messages[key];
                  }
              }
              if (e.type == "exception") {
                  servermap[e.server].addException(self.ticker.tick(e.time), e.text, e.time);
              }
              count++;
          }
  
          for (var i in messages) {
              var m = messages[i];
              m.markIncomplete();
              self.add(m);
              count++;
          }
  
          if (count != 0) {
              paper.setSize(self.tick_to_x(self.ticker.current()), 1000);
  
              var line = paper.path("M0 0 L0 1000");
              line.attr({"stroke": "red", "stroke-dasharray": "- "});
              var base = canvas.offsetLeft;// + ((canvas.offsetWidth - paper.width)/2);
              canvas.onmousemove = function (evt) {
                  var x = evt.screenX - base;
  
                  line.attr({"path": "M" + x + " 0 L" + x + " 1000"});
  
              };
  
              self.draw(paper);
              return true;
          } else {
              return false;
          }
      };
  
      var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + filter;
  
      LogGraph.loadData(asyncq, uri, processdata);
  };
  
  LogGraph.ServerGraph.server = function (graph, name) {
      this.graph = graph;
      this.serverid = graph.nextserverid++;
      this.name = name;
      this.y = (this.serverid * 300 + graph.serveroffset);
      this.states = new Array();
      this.exception = new Array();
  
      this.addState = function (state, time) {
          this.states.push([state, time]);
      }
  
      this.addException = function (tick, exception, time) {
          this.exception.push(new LogGraph.ServerGraph.exception(this.graph, tick, exception, time));
      }
  
      this.draw = function (paper) {
          var st = paper.set();
          st.push(paper.path("M0 " + this.y + " L" + paper.width + " " + this.y));
          st.push(paper.text(20, this.y - 10, this.name));
          st.attr({stroke: "gray"});
  
          var numstates = this.states.length;
  
          for (s = 0; s < numstates; s++) {
              var style = {};
              switch (this.states[s][0]) {
                  case "INIT":
                      style = {stroke: "yellow", "stroke-width": 3};
                      break;
                  case "FOLLOWING":
                      style = {stroke: "lightgreen", "stroke-width": 7};
                      break;
                  case "LEADING":
                      style = {stroke: "green", "stroke-width": 10};
                      break;
                  case "LOOKING":
                      style = {stroke: "orange", "stroke-width": 5};
                      break;
              }
              var startx = this.graph.tick_to_x(this.states[s][1]);
              var endx = s + 1 < numstates ? this.graph.tick_to_x(this.states[(s + 1)][1]) : paper.width;
              var p = paper.path("M" + startx + " " + this.y + " L" + endx + " " + this.y);
              p.attr(style);
          }
  
          for (e in this.exception) {
              this.exception[e].draw(paper, this);
          }
      }
  };
  
  LogGraph.ServerGraph.message = function (graph, src, srctime, dst, zxid) {
      this.graph = graph;
      this.src = src;
      this.srctime = srctime;
      this.dst = dst;
      this.dsttime = 0; //dsttime;
      this.name = "Unknown";
      this.zxid = zxid;
      this.moreinfo = "No extra information";
      this.incomplete = false;
  
      this.markIncomplete = function () {
          this.incomplete = true;
          this.dsttime = this.srctime;
      }
  
      this.draw = function (paper) {
          var srcx = this.graph.tick_to_x(this.srctime);
          var dstx = this.graph.tick_to_x(this.dsttime);
  
          var arrow = paper.set();
          var p = paper.path("M" + srcx + " " + this.src.y + " L" + dstx + " " + this.dst.y);
          arrow.push(p);
  
          var tx = (srcx + dstx) / 2;
          var ty = (this.src.y + this.dst.y) / 2;
          var t = paper.text(tx, ty, this.name);
  
          var gradiant = (this.dst.y - this.src.y) / (dstx - srcx);
          var angle = Math.atan(gradiant) * 57.2958;
          t.rotate(angle, true);
  
          var arrowl = paper.path("M" + dstx + " " + this.dst.y + " L" + (dstx - 10) + " " + this.dst.y);
          arrowl.rotate(angle + 20, dstx, this.dst.y);
          arrow.push(arrowl);
          var arrowr = paper.path("M" + dstx + " " + this.dst.y + " L" + (dstx - 10) + " " + this.dst.y);
          arrowr.rotate(angle - 20, dstx, this.dst.y);
          arrow.push(arrowr);
  
          arrow.attr({"stroke-width": 2, stroke: "gray"});
          if (this.incomplete) {
              arrow.attr({"stroke-dasharray": "- .", stroke: "pink", "stroke-width": 2});
          }
          arrow.mouseover(function (evt) {
              t.attr({"font-size": 20});
              arrow.attr({stroke: "red", "stroke-width": 3});
          });
          arrow.mouseout(function (evt) {
              t.attr({"font-size": 10});
  
              if (this.incomplete) {
                  arrow.attr({stroke: "pink", "stroke-width": 2});
              } else {
                  arrow.attr({stroke: "gray", "stroke-width": 2});
              }
          });
  
  
          arrow.click(function (evt) {
              var popup = document.createElement("div");
              popup.className = "popUp";
              popup.innerHTML = "zxid: " + parseInt(this.zxid).toString(16);
  
              popup.style.top = evt.clientY;
              popup.style.left = evt.clientX;
              document.body.appendChild(popup);
  
              popup.onclick = function (evt) {
                  document.body.removeChild(popup);
              };
          });
      }
  };
  
  LogGraph.ServerGraph.exception = function (graph, tick, exceptiontext, time) {
      this.graph = graph;
      this.time = time;
      this.text = exceptiontext;
      this.tick = tick;
  
      var self = this;
  
      this.draw = function (paper, server) {
          var center = this.graph.tick_to_x(this.tick);
          var p = paper.circle(center, server.y, 5);
          p.attr({stroke: "orange", fill: "red"});
  
          p.mouseover(function (evt) {
              p.popup = document.createElement("div");
              p.popup.className = "popUp";
              p.popup.innerHTML = self.text.replace("\n", "<br/>");
              ;
              p.popup.style.top = server.y + 50;
              p.popup.style.left = center + 25;
              document.body.appendChild(p.popup);
  
              p.animate({r: 10}, 500, "elastic");
          });
          p.mouseout(function (evt) {
              document.body.removeChild(p.popup);
              p.animate({r: 5}, 100);
          });
      }
  };
      
  