Вход Регистрация
Файл: js/al/graph.js
Строк: 2260
<?php
if (!window.Graph)
(function(
window) {

var 
DRAG_CURSOR = 'pointer',
  
DRAG2_CURSOR = 'pointer';
var 
document = window.document;

// Date utils
var MINUTE = 60, HOUR = 60 * MINUTE, DAY = 24 * HOUR, MONTH = 28 * DAY, YEAR = 365 * DAY, MAXSCALE = 2 * DAY + HOUR;
var 
xscales = [
  {
base: HOUR, format: ['hour'], sub: -1},
  {
base: 2 * HOUR, format: ['hour'], sub: HOUR},
  {
base: 3 * HOUR, format: ['hour'], sub: HOUR},
  {
base: 6 * HOUR, format: ['dayHour'], sub: HOUR},
  {
base: 8 * HOUR, format: ['dayHour'], sub: HOUR},
  {
base: 12 * HOUR, format: ['dayHour'], sub: HOUR},
  {
base: DAY, format: ['dayFull', 'day'], sub: -1},
  {
base: 2 * DAY, format: ['dayFull', 'day'], sub: DAY},
  {
base: 3 * DAY, format: ['dayFull', 'day'], sub: DAY},
  {
base: 4 * DAY, format: ['dayFull', 'day'], sub: DAY},
  {
base: MONTH, format: ['monthFull', 'month'], sub: -1},
  {
base: 2 * MONTH, format: ['monthFull', 'month'], sub: MONTH},
  {
base: 3 * MONTH, format: ['monthFull', 'month'], sub: MONTH},
  {
base: 4 * MONTH, format: ['monthFull', 'month'], sub: MONTH},
  {
base: 6 * MONTH, format: ['monthFull', 'month'], sub: MONTH},
  {
base: YEAR, format: ['yearFull'], sub: -1}]; // using 'year' ('00, '01, '02...) seems a bad idea
var DEF_TIME = Math.round((new Date()).getTime() / 1000);
var 
dateFormats = {
  
hour: "{hour}:00",
  
day: getLang('stats_day_mon').split("{month}").join("{dayMonth}"),
  
dayFull: getLang('stats_day_mon').split("{month}").join("{dayMonth}"),
  
dayFullYear: getLang('stats_day_month_year').split("{month}").join("{dayMonth}"),
  
dayHour: getLang('graph_day_fullmon_year_hour'),
  
dayHourMin: getLang('graph_day_fullmon_year_hour_min'),
  
month: "{shortMonth}'{shortYear}",
  
monthFull: "{month}'{shortYear}",
  
year: "'{shortYear}",
  
yearFull: "{year}"
};
var 
colors = [0x597da3, 0xb05c91, 0x4d9fab, 0x569567, 0xac4c4c, 0xc9c255, 0xcd9f4d, 0x876db3,
              
0x6f9fc4, 0xc77bb1, 0x70c5c8, 0x80bb88, 0xce5e5e, 0xe8e282, 0xedb24a, 0xae97d3,
              
0x6391bc, 0xc77bb1, 0x62b1bc, 0x80bb88, 0xb75454, 0xc9c255, 0xdca94f, 0x997fc4,
              
0x85afd0, 0xc77bb1, 0x8ecfce, 0x80bb88, 0xe47070, 0xc9c255, 0xf7be5a, 0xbeaadf];

var 
globalTouchCount = 0;

function 
fullDate(time, params) {
  var 
fmt = (params && params.show_time) ? ((params.show_minutes) ? 'dayHourMin' : 'dayHour') : 'dayFullYear';
  return 
formatDate(time, fmt);
}

function 
formatDate(time, format) {
  if (
time == -1) {
    var 
h = (format == 'dayHour' ? '0' : '88'), m = '88', d = '88',
      
mmmm = 'Mmmmmmmm', mmmm2 = 'Mmmmmmmm', mmm = 'mmm', mmm2 = 'mmm', yyyy = '88888', yy = '.88';
  } else {
    var 
date = new Date(time * 1000);
    var 
h = date.getHours(), m = date.getMinutes().toString();
    var 
h12 = h % 12;
    if (
h12 == 0) h12 = 12;
    var 
am_pm = h < 12 ? 'AM':'PM';
    
h = h.toString();
    if (
m.length == 1) m = '0' + m;
    var 
d = date.getDate().toString(),
    
mmmm = getLang('Month' + (date.getMonth() + 1) + '_of'), mmmm2 = getLang('Month' + (date.getMonth() + 1)),
    
mmm = mmmm.substr(0, 3).toLowerCase(), mmm2 = mmmm2.substr(0, 3).toLowerCase(),
    
yyyy = date.getFullYear().toString(), yy = yyyy.substr(2);
  }
  var 
fmt = dateFormats[format];
  return 
fmt.split('{month}').join(mmmm2).
    
split('{shortMonth}').join(mmm2).
    
split('{shortYear}').join(yy).
    
split('{year}').join(yyyy).
    
split('{day}').join(d).
    
split('{dayMonth}').join(mmmm).
    
split('{shortDayMonth}').join(mmm).
    
split('{hour}').join(h).
    
split('{hour12}').join(h12).
    
split('{min}').join(m).
    
split('{am/pm}').join(am_pm);
}

function 
incDate(time, step, inc) {
  var 
date = new Date(time * 1000);
  if (
step >= YEAR) {
    var 
count = step / YEAR;
    
date = new Date((Math.floor(date.getFullYear() / count) + inc) * count, 0, 1, 0, 0);
    return 
Math.floor(date.getTime() / 1000);
  } else
  if (
step < YEAR && step >= MONTH) {
    var 
count = step / MONTH;
    
date = new Date(date.getFullYear(), (Math.floor(date.getMonth() / count) + inc) * count, 1, 0, 0);
    return 
Math.floor(date.getTime() / 1000);
  } else
  if (
step < MONTH) {
    var 
tz = date.getTimezoneOffset() * 60;
    return 
Math.floor((time - tz) / step + inc) * step + tz;
  }
}

// Other utils
function addEventEx(graph, element, event, listener) {
  
element.graph = graph;
  
addEvent(element, event, listener);
}
function 
removeEventEx(element, event, listener) {
  
removeData(element, "graph");
  
removeEvent(element, event, listener);
}
function 
prepareEvent(event, pass) {
  if (
event.touches) {
    if (
event.touches.length > 1 || globalTouchCount > 1) {
      
event.currentTarget.graph.endDrag();
      return 
false;
    } else {
      if (!
pass) cancelEvent(event);
      return 
event.touches[0];
    }
  }
  
cancelEvent(event);
  return 
event;
}

function 
getContext(e) {
  var 
ctx = e.getContext("2d");
  if (!
ctx) return ctx;
  if (!
ctx.measureText || !ctx.measureText(getLang('Month1')))
    return 
null;
  return 
ctx;
}

function 
getFirstControlPoints(rhs) {
  var 
n = rhs.length, x = [], tmp = [], b = 2.0;
  
x[0] = rhs[0] / b;
  for (var 
i = 1; i < n; i++) {
    
tmp[i] = 1 / b;
    
b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
    
x[i] = (rhs[i] - x[i - 1]) / b;
  }
  for (var 
i = 1; i < n; i++)
    
x[n - i - 1] -= tmp[n - i] * x[n - i];
  return 
x;
}

function 
getYStep(maxValue, intScale) {
  var 
step = Math.pow(10, Math.floor(Math.LOG10E * Math.log(maxValue)));
  if (
maxValue / step <= 2)
    
step /= 4.0; else
  if (
maxValue / step <= 4)
    
step /= 2.0;

  if (
intScale) {
    
step = Math.max(step, 1);
    if (
step == 2.5) {
      
step = 2;
    }
  }
  return 
step;
}

function 
formatValue(value) { // something like -10 000 000.000001
  
var sgn = (value < 0) ? '-' : '';
  
value = Math.abs(value);
  var 
intv = Math.floor(value), fltv = value - intv;
  
fltv = Math.round(fltv * 100000.0) / 100000.0;
  var 
res = '';
  while (
intv > 0) {
    var 
q = (intv % 1000).toString();
    
res = (intv > 999 ? '000'.substr(0, 3 - q.length) : '') + q + (res == '' ? '' : ' ') + res;
    
intv = Math.floor(intv / 1000);
  }
  if (
res == '') res = '0';
  if (
fltv > 0) res += fltv.toString().substr(1);
  return 
sgn + res;
}

function 
drawCheck(e, color, color2, checked, over) {
  var 
ctx = getContext(e);
  
ctx.clearRect(0, 0, 20, 20);
  if (
checked) { // shadow
    
ctx.lineWidth = 2;
    
ctx.strokeStyle = color2;
    
ctx.beginPath();
    
ctx.moveTo(6.5, 11);
    
ctx.lineTo(9.5, 14);
    
ctx.lineTo(17.5, 6);
    
ctx.stroke();
  }
  if (
over) {
    
ctx.fillStyle = color2;
    
ctx.fillRect(3, 3, 14, 14);
  }
  
ctx.lineWidth = 1;
  
ctx.strokeStyle = color;
  
ctx.strokeRect(3.5, 3.5, 13, 13);
  if (
checked) {
    
ctx.lineWidth = 2;
    
ctx.beginPath();
    
ctx.moveTo(6.5, 10);
    
ctx.lineTo(9.5, 13);
    
ctx.lineTo(17.5, 5);
    
ctx.stroke();
  }
}

function 
drawLines(ctx, x, y, w, h, lines, stTime, enTime, xfactor, yfactor, yDelta, zoomedOut, active, smooth) {
  
smooth = (!zoomedOut) && smooth;
  var 
stIdxs = [], enIdxs = [], _lines = [];
  for (var 
l = 0; l < lines.length + (active ? 1 : 0); l++) {
    var 
line = (l < lines.length) ? (active != lines[l] ? lines[l] : null) : active;
    if (
line && line.shown) {
      var 
stIdx = 0, enIdx = line.d.length - 1;
      while ((
stIdx < line.d.length - 2) && (line.d[stIdx + 2].x < stTime)) stIdx++;
      while ((
enIdx > 2) && (line.d[enIdx - 2].x > enTime)) enIdx--;
      if (
enIdx - stIdx > w / 5) // dots overload
        
smooth = false;
      if (
enIdx >= stIdx && (line.d[enIdx].x >= stTime) && (line.d[stIdx].x <= enTime)) {
        
_lines.push(line);
          
stIdxs.push(stIdx);
        
enIdxs.push(enIdx);
        }
    }
  }

  
lines = _lines;
  for (var 
l = 0; l < lines.length; l++) {
    var 
stIdx = stIdxs[l], enIdx = enIdxs[l], ps = lines[l].d;

    if (
smooth && (enIdx - stIdx + 1 > 2)) {
    var 
knots = [], n = 0;
    for (var 
i = stIdx; i <= enIdx; i++) {
      
knots[n] = {X: x + (ps[i].x - stTime) * xfactor, Y: y + h - (ps[i].y + yDelta) * yfactor};
      
n++;
    }
    
n--;

    var 
rhs = [];
    for (var 
i = 1; i < n - 1; ++i)
      
rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X;
    
rhs[0] = knots[0].X + 2 * knots[1].X;
    
rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0;
    var 
xs1 = getFirstControlPoints(rhs);

    for (var 
i = 1; i < n - 1; ++i)
      
rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y;
    
rhs[0] = knots[0].Y + 2 * knots[1].Y;
    
rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0;
    var 
ys1 = getFirstControlPoints(rhs), xs2 = [], ys2 = [];
    for (var 
i = 0; i < n; ++i) {
      if (
i < n - 1) {
      
xs2[i] = 2 * knots[i + 1].X - xs1[i + 1];
      
ys2[i] = 2 * knots[i + 1].Y - ys1[i + 1];
      } else {
      
xs2[i] = (knots[n].X + xs1[n - 1]) / 2;
      
ys2[i] = (knots[n].Y + ys1[n - 1]) / 2;
      }
    }
    }

    
// fill
    
if (lines[l].f && stIdx < enIdx) {
      
ctx.globalAlpha = (active == null || active != lines[l]) ? 0.2 : 0.4;
      
ctx.fillStyle = lines[l].hexColor;
      
ctx.strokeStyle = lines[l].hexColor;
      
ctx.lineWidth = (zoomedOut ? 1 : 2);
      if (
lines[l].b) {
        for (var 
i = stIdx; i <= enIdx; i++) {
          var 
cx1 = Math.floor(x + ((i > stIdx ? (ps[i - 1].x + ps[i].x) / 2 : ps[i].x) - stTime) * xfactor);
          var 
cx2 = Math.floor(x + ((i < enIdx ? (ps[i].x + ps[i + 1].x) / 2 : ps[i].x) - stTime) * xfactor);
          var 
cy = (ps[i].y + yDelta) * yfactor;
          
ctx.fillRect(cx1, y + h - cy, cx2 - cx1, ps[i].y * yfactor);
        }
      } else {
        
ctx.beginPath();
        var 
px = x + (ps[stIdx].x - stTime) * xfactor;
        
ctx.moveTo(px, y + h - yDelta * yfactor);
        for (var 
i = stIdx; i <= enIdx; i++) {
        var 
cx = x + (ps[i].x - stTime) * xfactor;
        if (
smooth && (i > stIdx) && (enIdx - stIdx + 1 > 2))
          
ctx.bezierCurveTo(Math.min(Math.max(px, xs1[i - stIdx - 1]), cx), Math.min(y + h, Math.max(ys1[i - stIdx - 1], 0)), Math.min(Math.max(px, xs2[i - stIdx - 1]), cx), Math.min(y + h, Math.max(ys2[i - stIdx - 1], 0)), cx, y + h - (ps[i].y + yDelta) * yfactor);
        else
          
ctx.lineTo(cx, y + h - (ps[i].y + yDelta) * yfactor);
        
px = cx;
        }
        
ctx.lineTo(px, y + h - yDelta * yfactor);
        
ctx.fill();
      }
      
ctx.globalAlpha = 1;
    }

    
// stroke
    
ctx.strokeStyle = lines[l].hexColor;
    
ctx.lineWidth = (zoomedOut ? 1 : 2);
    
ctx.beginPath();
    var 
px = x + (ps[stIdx].x - stTime) * xfactor, py = y + h - (ps[stIdx].y + yDelta) * yfactor;
    
ctx.moveTo(px, py);
    if (
lines[l].b) {
      for (var 
i = stIdx; i <= enIdx; i++) {
        var 
cx = Math.floor(x + ((i < enIdx ? (ps[i].x + ps[i + 1].x) / 2 : ps[i].x) - stTime) * xfactor);
        var 
cy = y + h - (ps[i].y + yDelta) * yfactor;
        
ctx.lineTo(cx, cy);
        if (
i < enIdx)
        if (
zoomedOut)
          
ctx.lineTo(cx, y + h - (ps[i + 1].y + yDelta) * yfactor);
        else {
          
ctx.stroke();
          
ctx.beginPath();
          
ctx.moveTo(cx, y + h - (ps[i + 1].y + yDelta) * yfactor);
        }
      }
    } else {
      for (var 
i = stIdx + 1; i <= enIdx; i++) {
        var 
cx = x + (ps[i].x - stTime) * xfactor, cy = y + h - (ps[i].y + yDelta)* yfactor;
        if (
ps[i].s == 'x') {
          
ctx.stroke();
          
ctx.beginPath();
          
ctx.moveTo(cx, cy);
        } else {
          if (
ps[i].s == '-' || ps[i].s == '--' || ps[i].s == '.') {
            
ctx.stroke();
            
ctx.globalAlpha = 0.3;
            
ctx.beginPath();
            
ctx.moveTo(px, py);
          }

          if (
smooth && (i > stIdx) && (enIdx - stIdx + 1 > 2))
            
ctx.bezierCurveTo(Math.min(Math.max(px, xs1[i - stIdx - 1]), cx), Math.min(y + h, Math.max(ys1[i - stIdx - 1], 0)), Math.min(Math.max(px, xs2[i - stIdx - 1]), cx), Math.min(y + h, Math.max(ys2[i - stIdx - 1], 0)), cx, y + h - (ps[i].y + yDelta) * yfactor);
          else
            
ctx.lineTo(cx, y + h - (ps[i].y + yDelta) * yfactor);

          if (
ctx.globalAlpha != 1) {
            
ctx.stroke();
            
ctx.beginPath();
            
ctx.moveTo(cx, cy);
          }
        }
        
ctx.globalAlpha = 1;
        
px = cx;
        
py = cy;
      }
    }
    
ctx.stroke();

    
// dots
    
if (smooth || (enIdx - stIdx < 2 && !zoomedOut)) {
      
ctx.strokeStyle = lines[l].hexColor;
      
ctx.fillStyle = '#ffffff';
      
ctx.lineWidth = 2;
      for (var 
i = stIdx; i <= enIdx; i++) {
        
ctx.beginPath();
        
ctx.arc(x + (ps[i].x - stTime) * xfactor, y + h - (ps[i].y + yDelta) * yfactor, 3.5, 0, Math.PI*2, true);
        
ctx.fill();
        
ctx.stroke();
      }
    }
  }
}

function 
drawBars(ctx, x, y, w, h, lines, labels, barW, xfactor, yfactor, yDelta) {
  for (var 
l = 0; l < lines.length; l++) {
    var 
ps = lines[l].d;
    for (
i = 0; i < ps.length; i++) {
      if (!
labels[ps[i].x]) {
        continue;
      }
      var 
cx = Math.floor(getBarXValue(x, xfactor, barW, l, ps[i].x));
      var 
psy = Math.max(ps[i].y, 2 / yfactor);
      var 
cy = (psy + yDelta) * yfactor;

      
ctx.fillStyle = lines[l].hexColor;
      
ctx.fillRect(cx, Math.floor(y + h - cy), barW, Math.ceil(psy * yfactor));
    }
  }
}

function 
getBarXValue(xOffset, xStep, barsWidth, lineIndex, barIndex) {
  return 
xOffset + barsWidth / 2 + lineIndex * barsWidth + barIndex * xStep;
}

function 
clearSelection() {
  if(
document.selection && document.selection.empty) {
    
document.selection.empty();
  } else if(
window.getSelection) {
    var 
sel = window.getSelection();
    
sel.removeAllRanges();
  }
}

function 
dataObjectToArray(data_object) {
  var 
data_array = [];
  for (var 
name in data_object) {
    
data_array.push(data_object[name]);
  }
  return 
data_array;
}

// Graph constructor

function Graph(id, data, params, width, height) {
  var 
graph = this;
  
params = extend({}, params);
  
width = width ? width : 585;
  
height = height ? height : 385;
  
extend(this, {params: params, width: width, height: height, viewWidth: width, viewHeight: height - 80});
  
this.params.yunits = this.params.yunits || '';

  
// create basic DOM tree
  
this.graphDiv = ge(id);
  
setStyle(this.graphDiv, {fontFamily: 'tahoma, arial, verdana, sans-serif, Lucida Sans', fontSize: '11px', color: '#36638e', width: this.viewWidth + 'px', clear: 'both', lineHeight: '130%'});
  
this.graphDiv.innerHTML = '<div style="float: left"><canvas style="padding-top: 20px"></canvas></div>
    <div style="float: left">
      <div style="height: 20px; float: right;"></div>
      <div style="height: 20px; padding-left: 10px;"></div>
      <div style="position: relative; height: ' 
+ this.viewHeight + 'px;">
        <canvas height="' 
+ this.viewHeight + '"></canvas>
        <canvas height="' 
+ (this.viewHeight - 17) + '" style="position: absolute; top: 0; left: 0;"></canvas>
        <div style="position: absolute; border: 1px solid #5f7d9d; color: #5f7d9d;
            background-color: #eeeeee; padding: 2px 3px 2px 4px; white-space: nowrap; display: none;">' 
+ cur.graphVars['lang.data_empty'] + '</div>
      </div>
      <div style="position: relative; height: 40px;">
        <canvas height="40"></canvas>
        <div style="position: absolute; top: 0; height: 38px; border: 1px solid #b8c5d4; border-width: 1px 0px 1px 1px; border-top-color: #d3dee8;"><div style="background: white; opacity: 0.5; filter: alpha(Opacity=50); width: 100%; height: 100%;"></div></div>
        <div style="position: absolute; top: 0; height: 38px; border: 1px solid #b8c5d4; cursor: ' 
+ DRAG_CURSOR + ';"></div>
        <div style="position: absolute; top: 0; height: 38px; border: 1px solid #b8c5d4; border-width: 1px 1px 1px 0px; border-top-color: #d3dee8;"><div style="background: white; opacity: 0.5; filter: alpha(Opacity=50); width: 100%; height: 100%;"></div></div>
        <div style="position: absolute; top: 0; height: 38px; border: 1px solid #b8c5d4; background: #ced9de; opacity: 0.5; filter: alpha(Opacity=50); display: none;"></div>
          <div style="position: absolute; top: 0; width: 8px; height: 40px; cursor: w-resize;"></div>
          <div style="position: absolute; top: 0; width: 8px; height: 40px; cursor: e-resize;"></div>
      </div>
      <div style="float: left; width: 270px; padding-top: 8px; -moz-user-select: none; -khtml-user-select: none;"></div><div style="float: left; width: 270px; padding-top: 8px; -moz-user-select: none; -khtml-user-select: none;"></div>
    </div><div style="clear: left;"></div>'
;

  
this.vScale = this.graphDiv.children[0];
  
this.vScaleView = this.vScale.children[0];
  
this.mainLayout = this.graphDiv.children[1];
  
this.menu = this.mainLayout.children[0];
  
this.title = this.mainLayout.children[1];
  
this.zinLayout = this.mainLayout.children[2];
  
this.zinView = this.zinLayout.children[0];
  
this.dotsLayer = this.zinLayout.children[1];
  
this.message = this.zinLayout.children[2];
  
this.zoutLayout = this.mainLayout.children[3];
  
this.zoutView = this.zoutLayout.children[0];
  
this.lMask = this.zoutLayout.children[1];
  
this.zoutWindow = this.zoutLayout.children[2];
  
this.rMask = this.zoutLayout.children[3];
  
this.select = this.zoutLayout.children[4];
  
this.lHandle = this.zoutLayout.children[5];
  
this.rHandle = this.zoutLayout.children[6];
  
this.column = [this.mainLayout.children[4], this.mainLayout.children[5]];

  var 
defaultGraph = 0;
  if (
typeof params.multiple == 'object' && params.multiple.items) {
    if (!
params.multiple.def) params.multiple.def = 0;
    for (
i in params.multiple.items) {
      var 
item = se('<a class="graph_menu_item' + (i == params.multiple.def ? ' graph_menu_item_sel' : '') + '">' + params.multiple.items[i] + '</a>');
      var 
clickContext = {clickNode: i, graph: this};
      
addEvent(item, 'click', this.onClickMenu, null, clickContext);
      
cur.destroy.push(function() {
        
removeEvent(item, 'click', this.onClickMenu, null, clickContext);
      });
      
this.menu.appendChild(item);
    }
    if (
params.multiple.graph_item) {
      var 
item = se('<a class="graph_menu_item">' + params.multiple.graph_item + '</a>');
      var 
clickContext = {clickNode: params.multiple.items.length, graph: this, alt_graph: true};
      
addEvent(item, 'click', this.onClickMenu, null, clickContext);
      
cur.destroy.push(function() {
        
removeEvent(item, 'click', this.onClickMenu, null, clickContext);
      });
      
this.menu.appendChild(item);
    }

    for (
i in params.multiple.altitems) {
      var 
item = se('<a class="graph_menu_item' + (i + params.multiple.altitems.length == params.multiple.def ? ' graph_menu_item_sel' : '') + '">' + params.multiple.altitems[i][0] + '</a>');
      var 
clickContext = {clickNode: i, graph: this, force_func: params.multiple.altitems[i][1] };
      
addEvent(item, 'click', this.onClickMenu, null, clickContext);
      
cur.destroy.push(function() {
        
removeEvent(item, 'click', this.onClickMenu, null, clickContext);
      });
      
this.menu.appendChild(item);
    }
    
defaultGraph = params.multiple.def;
    
params.multiple = 1;
  }

  
extend(this, {
    
colMax: [0, 0], lines: [],
    
leftTime: -1, rightTime: -1,
    
ymarks: [],
    
ystep: 0,
    
minValue: 100, maxValue: 0, minTime: 2147483647, maxTime: 0,
    
dragTime: [],
    
startDrag: -1, startDrag2: -1, startValue: -1, startValue2: -1, endValue: -1,
    
dragElement: null, maskDragging: false, viewClick: false, smoothLines: false,
    
scaleWidth: 0, activeLine: null, isNegative: false});

  try {
    var 
tctx = getContext(this.zinView);
    if (!
tctx) throw 'Error';
  } catch (
e) {
    
this.zoutLayout.style.display = 'none';
    
this.vScale.style.display = 'none';
    
this.title.style.display = 'none';
    
this.message = (this.message == null) ? this.zinLayout.children[0] : this.message;
    
this.message.innerHTML = (cur.graphVars['lang.bad_browser'] || 'Для отображения графика статистики Вам необходимо {link}обновить браузер{/link}.').replace('{link}', '<a onclick="Graph.prototype.showBadBrowserBox();">').replace('{/link}', '</a>');
    
setStyle(this.graphDiv, {backgroundColor: '#F7F7F7', width: '480px', margin: '0 auto'});
    
setStyle(this.message, {
      
left: Math.round((getSize(this.zinLayout)[0] - getSize(this.message)[0]) / 2) + 'px',
      
top: Math.round((getSize(this.zinLayout)[1] - getSize(this.message)[1]) / 2) + 'px',
      
display: 'block',
      
color: '#707070',
      
border: 'none',
      
backgroundColor: 'transparent'
    
});
    if (!
cur.badBrowserBox) {
      
this.showBadBrowserBox();
    }
    return 
null;
  }

  if (
vk.al) {
    
stManager.add(['tooltips.js', 'tooltips.css'], function() {
      
tooltips.create(graph.mainLayout, {text: '...', black: 1, forcetoup: 1, nohide: 1});
    });
  } else {
    
tooltips = false;
  }

  if (!
params.bar_chart) {
    
addEventEx(this, this.lHandle, 'mousedown touchstart', this.takeHandle);      // resize window
    
addEventEx(this, this.rHandle, 'mousedown touchstart', this.takeHandle);
    
addEventEx(this, this.zoutWindow, 'mousedown touchstart', this.takeWindow);      // drag window
    
addEventEx(this, this.lMask, 'mousedown touchstart', this.takeMask);        // select new window
    
addEventEx(this, this.rMask, 'mousedown touchstart', this.takeMask);
    
addEventEx(this, this.zinLayout, 'mousedown touchstart', this.takeView);      // drag view
    
addEventEx(this, this.zinLayout, 'mousewheel DOMMouseScroll', this.wheelView);    // zoom with mousewheel
    
addEventEx(this, this.zoutLayout, 'mousewheel DOMMouseScroll', this.wheelView);
  }
  
addEventEx(this, this.dotsLayer, 'mousemove', this.showDots);              // show hints
  
addEventEx(this, this.zinLayout, 'mouseout', this.hideDots);            // hide hints
  
addEvent(this.column[0], 'selectionstart', cancelEvent);
  
addEvent(this.column[1], 'selectionstart', cancelEvent);

  if (
typeof(data) != 'string') {
    
graph.loadGraph = function(index) {
      
graph.setData(params.multiple ? (data[index] || []) : data, isArray(params.adjust) ? params.adjust[index] : params.adjust);
    }
  }

  if (
params.multiple) {
    for (var 
i = 0; i < data.length; i++) {
      
data[i] = dataObjectToArray(data[i]);
    }

    
graph.setData((typeof(data) == 'string') ? data : (data[defaultGraph] || []), isArray(params.adjust) ? params.adjust[0] : params.adjust);
  } else {
    
graph.setData((typeof(data) == 'string') ? data : dataObjectToArray(data), params.adjust);
  }
  return 
graph;
}

// Prototype methods
Graph.prototype = {
  
getXValue: function(pageX, view) {
    return (
view ?
      ((
pageX - getXY(this.zinView)[0]) / this.xfactorIn + this.leftTime) :
      ((
pageX - getXY(this.zoutView)[0]) / this.xfactorOut + this.minTime));
  },
  
allowZoom: function(zoom_allowed) {
    
this.zoutLayout.style.display = zoom_allowed ? 'block' : 'none';
    
this.viewHeight = this.height - (zoom_allowed ? 80 : 40);
    
this.zinLayout.style.height = this.viewHeight + "px";
    
this.zinView.height = this.viewHeight;
    
this.dotsLayer.height = this.viewHeight - 17;
    
this.disable_zoom = !zoom_allowed;
  },
  
showEmpty: function(no_data) {
    if (
no_data) this.allowZoom(false);
    
setStyle(this.message, {
      
display: no_data ? 'block' : 'none',
      
left: Math.round((this.viewWidth - getSize(this.message)[0]) / 2) + 'px',
      
top: Math.round((this.viewHeight - getSize(this.message)[1]) / 2) + 'px'});
  },
  
overrideCursor: function(cursor) {
    
document.body.style.cursor = cursor ? cursor : 'default';
    
//document.body.style.cursor = cursor ? cursor : 'auto'; // Opera don't understand 'auto'
    
this.lHandle.style.cursor = cursor ? cursor : 'w-resize';
    
this.rHandle.style.cursor = cursor ? cursor : 'e-resize';
    
this.zoutWindow.style.cursor = cursor ? cursor : DRAG_CURSOR;
  },
  
takeHandle: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (
g.disable_zoom) return;
    if (!(
ev = prepareEvent(ev))) return;
    
addEventEx(g, document, 'mousemove drag touchmove', g.dragHandle);
    
addEventEx(g, document, 'mouseup touchend', g.endDrag);
    
g.startDrag = ev.pageX;
    
g.startValue = (ev.currentTarget == g.lHandle) ? g.leftTime : g.rightTime;
    
g.dragElement = ev.currentTarget;
    
g.hideDots(ev);
    
g.overrideCursor(g.dragElement.style.cursor);
    return 
false;
  },
  
takeWindow: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (
g.disable_zoom) return;
    if (!(
ev = prepareEvent(ev, true))) return;
    
addEventEx(g, document, 'mousemove drag touchmove', g.dragWindow);
    
addEventEx(g, document, 'mouseup touchend', g.endDrag);
    
g.startDrag = ev.pageX;
    
g.startValue = g.leftTime;
    
g.hideDots(ev);
    
g.overrideCursor(DRAG2_CURSOR);
    return 
false;
  },
  
takeMask: function(ev) {
    
cancelEvent(ev);
    var 
g = ev.currentTarget.graph;
    if (
g.disable_zoom) return;
    
g.dragElement = ev.currentTarget;
    if (!(
ev = prepareEvent(ev))) return;
    
addEventEx(g, document, 'mousemove drag touchmove', g.dragMask);
    
addEventEx(g, document, 'mouseup touchend', g.endDrag);
    
g.startValue = (ev.pageX - getXY(g.dragElement.parentNode)[0]) / g.xfactorOut + g.minTime;
    
g.endValue = g.startValue;
    
g.hideDots(ev);
    return 
false;
  },
  
takeView: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (
g.disable_zoom) {
      
g.deselectLine(ev);
      return;
    }
    if (!(
ev = prepareEvent(ev, true))) return;
    
g.dragTime[0] = g.getXValue(ev.pageX, true);
    
addEventEx(g, document, 'mousemove drag touchmove', g.dragView);
    
addEventEx(g, document, 'mouseup touchend', g.endDrag);
    
g.overrideCursor(DRAG2_CURSOR);
    
g.viewClick = true;
    
g.startDrag = ev.pageX;
    
g.showDots(ev);
    
//return false;
  
},
  
dragHandle: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (!(
ev = prepareEvent(ev))) return;
    var 
xfactorOut = g.viewWidth / (g.maxTime - g.minTime);
    if (
g.dragElement == g.lHandle) {
      
g.leftTime = Math.max(g.minTime, Math.min(g.rightTime - g.MAXSCALE, g.startValue - (g.startDrag - ev.pageX) / xfactorOut));
    } else {
      
g.rightTime = Math.min(g.maxTime, Math.max(g.leftTime + g.MAXSCALE, g.startValue - (g.startDrag - ev.pageX) / xfactorOut));
    }
    
g.redrawWindow();
    return 
false;
  },
  
dragWindow: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (!(
ev = prepareEvent(ev))) return;
    var 
xfactorOut = g.viewWidth / (g.maxTime - g.minTime),
    
diff = g.leftTime - (g.startValue - (g.startDrag - ev.pageX) / xfactorOut);
    
diff = Math.max(diff, g.rightTime - g.maxTime);
    
diff = Math.min(diff, g.leftTime - g.minTime);

    
g.leftTime -= diff;
    
g.rightTime -= diff;
    
g.redrawWindow();
    return 
false;
  },
  
dragMask: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (!(
ev = prepareEvent(ev))) return;
    var 
xfactorOut = g.viewWidth / (g.maxTime - g.minTime);
    
g.endValue = (ev.pageX - getXY(g.dragElement.parentNode)[0]) / xfactorOut + g.minTime;
    var 
lpos = (Math.max(g.minTime, Math.min(g.startValue, g.endValue)) - g.minTime) * xfactorOut,
      
rpos = (Math.min(g.maxTime, Math.max(g.startValue, g.endValue)) - g.minTime) * xfactorOut;
    if (!
g.maskDragging && (rpos - lpos > 4)) {
      
g.select.style.display = 'block';
      
g.maskDragging = true;
    }
    
g.select.style.left = lpos + "px";
    
g.select.style.width = (rpos - lpos - 2) + "px";

    return 
false;
  },
  
dragView: function(ev) {
    var 
g = ev.currentTarget.graph;
    if (!(
ev = prepareEvent(ev, g.viewClick))) return;
    var 
diff = (ev.pageX - getXY(g.zoutView)[0]) / g.xfactorIn + g.leftTime - g.dragTime[0];
    
diff = Math.max(diff, g.rightTime - g.maxTime);
    
diff = Math.min(diff, g.leftTime - g.minTime);
    
g.leftTime -= diff;
    
g.rightTime -= diff;

    if (
Math.abs(ev.pageX - g.startDrag) > 4)
      
g.viewClick = false;
    
g.redrawWindow();
    
g.showDots(ev);
    return 
g.viewClick;
  },
  
endDrag: function(ev) {
    var 
g = this;
    if (
ev) {
      
cancelEvent(ev);
      
g = ev.currentTarget.graph;
    }
    
removeEventEx(document, 'mousemove drag touchmove', g.dragHandle);
    
removeEventEx(document, 'mousemove drag touchmove', g.dragWindow);
    
removeEventEx(document, 'mousemove drag touchmove', g.dragMask);
    
removeEventEx(document, 'mousemove drag touchmove', g.dragView);
    
removeEventEx(document, 'mouseup touchend', g.endDrag);
    
g.select.style.display = 'none';
    
g.overrideCursor();
    if (
g.endValue != -1) {
      if (
g.maskDragging) {
        
g.leftTime = Math.min(g.startValue, g.endValue);
        
g.rightTime = Math.min(g.maxTime, Math.max(Math.max(g.startValue, g.endValue), g.leftTime + g.MAXSCALE));
        
g.leftTime = Math.max(g.minTime, Math.min(g.leftTime, g.rightTime - g.MAXSCALE));
      } else {
        var 
diff = (g.rightTime - g.leftTime) / 2.0;
        
g.leftTime = (g.endValue - diff);
        
g.rightTime = (g.endValue + diff);
        if (
g.leftTime < g.minTime) {
          
diff = g.minTime - g.leftTime;
          
g.leftTime += diff;
          
g.rightTime += diff;
        } else
        if (
g.rightTime > g.maxTime) {
          
diff = g.rightTime - g.maxTime;
          
g.leftTime -= diff;
          
g.rightTime -= diff;
        }
      }
    }
    if (
g.viewClick)
      
g.deselectLine(ev);
    else
      
g.redrawWindow();
    
g.endValue = -1;
    
g.maskDragging = false;
    
g.viewClick = false;
    
g.dragTime = [];
    return 
false;
  },
  
wheelView: function(ev) {
    var 
g = ev.currentTarget.graph,
      
delta = (ev.detail ? -ev.detail * 50 : ev.wheelDelta),
      
dist = g.rightTime - g.leftTime, diffL = (delta * dist) / 3500, diffR;
    if (
g.disable_zoom) return;
      if (
g.lines.length == 0) return;
    
diffL = Math.min(diffL, (dist - g.MAXSCALE) / 2);
    
diffR = Math.max(g.minTime - g.leftTime - diffL, 0);
    
g.leftTime = Math.max(g.minTime, g.leftTime + diffL);
    
g.rightTime = Math.min(g.maxTime, g.rightTime + diffR);
    
g.redrawWindow();
    
g.showDots(ev);
    return 
cancelEvent(ev);
  },
  
overLegend: function(ev) {
    var 
line = ev.currentTarget.line;
    
drawCheck(line.legendCheck, line.hexColor, line.hexColor2, line.shown, true);
  },
  
outLegend: function(ev) {
    var 
line = ev.currentTarget.line;
    
drawCheck(line.legendCheck, line.hexColor, line.hexColor2, line.shown, false);
  },
  
toggleLegend: function(ev) {
    var 
g = ev.currentTarget.graph, line = ev.currentTarget.line, noneShown = true;
    for (var 
l = 0; l < g.lines.length; l++)
      if (
g.lines[l] != line && g.lines[l].shown)
        
noneShown = false;
    
line.shown = noneShown || !line.shown;
    
drawCheck(line.legendCheck, line.hexColor, line.hexColor2, line.shown, true);
    
g.updateLines();
    
clearSelection();
    return 
cancelEvent(ev);
  },
  
chooseLegend: function(ev) {
    var 
g = ev.currentTarget.graph, line = ev.currentTarget.line, onlyThis = line.shown;
    if (
onlyThis)
      for (var 
l = 0; l < g.lines.length; l++)
        if (
g.lines[l] != line && g.lines[l].shown) {
          
onlyThis = false;
          break;
        }

    for (var 
l = 0; l < g.lines.length; l++) {
      
g.lines[l].shown = onlyThis || (g.lines[l] == line);
      
drawCheck(g.lines[l].legendCheck, g.lines[l].hexColor, g.lines[l].hexColor2, g.lines[l].shown, g.lines[l] == line);
    }
    
g.updateLines();
    
clearSelection();
    return 
cancelEvent(ev);
  },
  
showDots: function(ev) {
    var 
g = ((ev && ev.currentTarget) ? ev.currentTarget.graph : this);
    if (
g.params.bar_chart) {
      
g.highlightBar(ev);
      return;
    }
    var 
nearTime = -1, nnd = 0, cx = g.getXValue(ev.pageX, true);
    var 
ccx = ev.pageX - getXY(g.zinView)[0], ccy = ev.pageY - getXY(g.zinView)[1];
    var 
max_coltext = [0, 0];
    var 
activeLine = -1;
    var 
ctx = getContext(g.dotsLayer);
    
ctx.clearRect(0, 0, g.viewWidth, g.viewHeight);

    for (var 
l = 0; l < g.lines.length; l++) {
      var 
ni = -1, nd = 0;
      if (
g.lines[l].shown) {
        for (var 
i = g.lines[l].stIdx; i <= g.lines[l].enIdx; i++)
          if (
g.lines[l].d[i].x >= g.leftTime && g.lines[l].d[i].x <= g.rightTime && (ni == -1 || nd > Math.abs(g.lines[l].d[i].x - cx))) {
            
ni = i;
            
nd = Math.abs(g.lines[l].d[i].x - cx);
          }
      }
      if (
ni > -1) {
        var 
dot = g.lines[l].d[ni],
            
dotX = (dot.x - g.leftTime) * g.xfactorIn,
            
dotY = g.viewHeight - (g.isNegative ? (34 + (dot.y - g.localBottom) * g.yfactorIn) : (25 + (dot.y - (g.adjust ? g.localBottom : 0)) * g.yfactorIn));
        
g.lines[l].dot.posX = dotX;
        
g.lines[l].dot.posY = dotY;
        if (
ccx >= dotX - 7 && ccx <= dotX + 9 &&
            
ccy >= dotY - 7 && ccy <= dotY + 9) {
          
activeLine = l;
        }
        if (
nearTime == -1 || nd < nnd) {
          
nnd = nd;
          
nearTime = g.lines[l].d[ni].x;
        }

        
ctx.fillStyle = g.lines[l].hexColor;
        
ctx.beginPath();
        
ctx.arc(dotX, dotY, 3, 0, Math.PI*2, true);
        
ctx.fill();

        if (!
isVisible(g.lines[l].dotLabel) || !g.lines[l].dotLabel.dot || g.lines[l].dotLabel.dot.x != dot.x || parseInt(g.lines[l].dot.style.left) != Math.floor(dotX)) {
          
g.lines[l].dotLabel.innerHTML = dot.l ? dot.l : formatValue(dot.y) + g.params.yunits;
          var 
labelW = getSize(g.lines[l].dotLabel)[0];
          var 
pos = Math.max(Math.min(-labelW / 2.0, -labelW + g.viewWidth - dotX - 2), 2 - dotX);
          
g.lines[l].dotLabel.dot = dot;
          
g.lines[l].dotLabel.pos = Math.floor(pos);
          
g.lines[l].dotLabel.w = labelW;

          
setStyle(g.lines[l].dot, {
            
left: Math.floor(dotX) + "px",
            
top: Math.floor(dotY) + "px"});
          
setStyle(g.lines[l].dotLabel, {
            
top: ((g.lines[l].dot.posY > 26) ? -26 : 6) + "px",
            
left: g.lines[l].dotLabel.pos + "px"});
        }

        
g.lines[l].legendBox.innerHTML = formatValue(dot.y) + g.params.yunits;
        
g.lines[l].legendBox.style.display = 'block';
      } else {
        
g.lines[l].legendBox.style.display = 'none';
      }
      if (
g.lines[l].legendBox.style.left == '40px') { // label position fix in legend
        
var col = l < g.lines.length / 2 ? 0 : 1;
        
max_coltext[col] = Math.max(max_coltext[col], getSize(g.lines[l].legend.children[1])[0]);
      }
    }
    if (!
g.smoothLines || !tooltips) {
      
activeLine = -1;
    }
    for (var 
l = 0; l < g.lines.length; l++) {
      if (!
g.lines[l].shown) {
        
hide(g.lines[l].dot);
        continue;
      }
      if (
activeLine == l) {
        
g.showDotTT(l);
      } else if (
activeLine == -1) {
        if (
g.activeLine == null || g.activeLine == g.lines[l]) {
          
show(g.lines[l].dot);
        } else {
          
hide(g.lines[l].dot);
        }
      } else {
        
hide(g.lines[l].dot);
      }
    }

    if (
max_coltext[0] || max_coltext[1]) {
      for (var 
l = 0; l < g.lines.length; l++) {
        var 
col = l < g.lines.length / 2 ? 0 : 1;
        if (
max_coltext[col]) {
          
g.lines[l].legendBox.style.left = (max_coltext[col] + 40) + 'px';
        }
      }
    }
    if (
activeLine == -1 && tooltips) {
      
tooltips.hide(g.mainLayout, {fasthide: 1});
    }
    if (
nearTime > -1 && isVisible(g.title))
      
g.title.innerHTML = fullDate(nearTime, g.params);
  },
  
hideDots: function(ev) {
    var 
g = (ev.currentTarget ? ev.currentTarget.graph : this), vwPos = getXY(g.zinView);
    if (
ev.pageX >= vwPos[0] && ev.pageX < vwPos[0] + g.viewWidth && ev.pageY >= vwPos[1] && ev.pageY < vwPos[1] + g.viewHeight) return;

    for (var 
l = 0; l < g.lines.length; l++) {
      
hide(g.lines[l].dot);
      if(
g.lines[l].sum) {
        
g.lines[l].legendBox.innerHTML = formatValue(g.lines[l].sum) + g.params.yunits;
      } else {
        
hide(g.lines[l].legendBox);
      }
    }
    var 
ctx = getContext(g.dotsLayer);
    
ctx.clearRect(0, 0, g.viewWidth, g.viewHeight);
    
g.title.innerHTML = fullDate(g.localLeft) + (g.localRight > g.localLeft ? ' &ndash; ' + fullDate(g.localRight) : '');
    if (
tooltips) {
      
tooltips.hide(g.mainLayout, {fasthide: 1});
    }
  },
  
highlightBar: function(ev) {
    var 
g = ((ev && ev.currentTarget) ? ev.currentTarget.graph : this);
    var 
cx = (ev.pageX - getXY(g.zinView)[0]);
    var 
cy = (ev.pageY - getXY(g.zinView)[1]);
    var 
ctx = getContext(g.dotsLayer);
    
ctx.clearRect(0, 0, g.viewWidth, g.viewHeight);

    var 
activeLine = activeBar = -1;
    for (var 
l = 0; l < g.lines.length; l++) {
      for (var 
i = 0; i < g.lines[l].d.length; i++) {
        var 
nx = getBarXValue(0, g.xstep, g.barsWidth, l, g.lines[l].d[i].x);
        var 
ny = g.viewHeight - (g.isNegative ? (34 + (g.lines[l].d[i].y - g.localBottom) * g.yfactorIn) : (25 + (g.lines[l].d[i].y - (g.adjust ? g.localBottom : 0)) * g.yfactorIn));
        if (
cx > nx && cx <= nx + g.barsWidth && cy >= ny) {
          
activeLine = l;
          
activeBar = i;
          break;
        }
      }
    }
    for (var 
l = 0; l < g.lines.length; l++) {
      if (
activeBar > -1) {
        var 
dot = g.lines[l].d[activeBar],
            
dotX = getBarXValue(0, g.xstep, g.barsWidth, l, dot.x),
            
dotY = g.viewHeight - (g.isNegative ? (34 + (dot.y - g.localBottom) * g.yfactorIn) : (25 + (dot.y - (g.adjust ? g.localBottom : 0)) * g.yfactorIn)),
            
val = formatValue(dot.y) + g.params.yunits;

        if (
activeLine == l) {
          
ctx.fillStyle = g.lines[l].hexColor3;
          
ctx.fillRect(dotX, dotY, g.barsWidth, g.viewHeight);

          
g.showTT(dotX, dotY, dot.l ? dot.l : val + '<br>' + g.lines[l].name + ', ' + this.params.bar_chart[activeBar]);
          
g.lines[l].legendBox.style.display = 'block';
          
g.lines[l].legendBox.innerHTML = val;
        } else {
          
g.lines[l].legendBox.style.display = 'block';
          
g.lines[l].legendBox.innerHTML = val;
        }
      } else if(
g.lines[l].sum) {
        
g.lines[l].legendBox.innerHTML = formatValue(g.lines[l].sum) + g.params.yunits;
      } else {
        
g.lines[l].legendBox.style.display = 'none';
      }
    }
    if (
activeBar == -1 && tooltips) {
      
tooltips.hide(g.mainLayout, {fasthide: 1});
    }
  },
  
expandLabel: function(ev) {
    var 
g = ev.currentTarget.graph, line = ev.currentTarget.line, dot = line.dotLabel.dot, pos = line.dotLabel.pos;
    if (!
g.smoothLines) {
      var 
text = '';
      if (
line.l) {
        
text += langNumeric(dot.y, line.l, true);
      } else {
        
text += (dot.l ? dot.l : formatValue(dot.y)) + ' ' + line.name;
      }
      
line.dotLabel.innerHTML = text;
      var 
sz = getSize(line.dotLabel)[0];
      if (
pos + sz + line.dot.posX > g.viewWidth) {
        
line.dotLabel.innerHTML = line.name + ' ' + (dot.l ? dot.l : formatValue(dot.y));
        
line.dotLabel.style.left = pos - (sz - line.dotLabel.w) + "px";
      } else
        
line.dotLabel.style.left = pos + "px";

      for (var 
l = 0; l < g.lines.length; l++)
        
g.lines[l].dotLabel.style.zIndex = (g.lines[l] == line) ? 1000 : 200;
    }
    return 
cancelEvent(ev);
  },
  
chideLabel: function(ev) {
    var 
g = ev.currentTarget.graph, line = ev.currentTarget.line,
      
locX = ev.pageX - (getXY(line.dot)[0] + line.dotLabel.pos);
    if (
locX < 0 || locX > line.dotLabel.w)
      
g.hideLabel(ev);
  },
  
hideLabel: function(ev) {
    var 
g = ev.currentTarget.graph, line = ev.currentTarget.line, dot = line.dotLabel.dot, pos = line.dotLabel.pos;
    
line.dotLabel.innerHTML = dot.l ? dot.l : formatValue(dot.y);
    
line.dotLabel.style.left = pos + "px";
  },
  
selectLine: function(ev) {
    var 
g = ev.currentTarget.graph, line = ev.currentTarget.line;
    
g.activeLine = line;
    
g.redrawWindow();
    
g.showDots(ev);
    return 
cancelEvent(ev);
  },
  
deselectLine: function(ev) {
    
this.activeLine = null;
    
this.redrawWindow();
    
this.showDots(ev);
    return 
cancelEvent(ev);
  },
  
showDotTT: function(lineIndex) {
    if (!
this.smoothLines) {
      return;
    }
    if (
this.ttLine != lineIndex) {
      
tooltips.hide(this.mainLayout, {fasthide: 1});
    }

    var 
line = this.lines[lineIndex], dot = line.dotLabel.dot;
    var 
text = '';
    if (
line.l) {
      
text += langNumeric(dot.y, line.l, true);
    } else {
      
text += (dot.l ? dot.l : formatValue(dot.y)) + ' &ndash; ' + line.name;
    }
    
text += '<br/><span style="font-weight: normal; font-size: 0.9em;">' + fullDate(dot.x, this.params) + '</span>';

    for (var 
l = 0; l < this.lines.length; l++) {
      
hide(this.lines[l].dot);
    }
    
this.showTT(line.dot.posX + 1, line.dot.posY - 5, text);
    
this.ttLine = lineIndex;

    var 
ctx = getContext(this.dotsLayer);
    
ctx.strokeStyle = line.hexColor;
    
ctx.globalAlpha = 0.3;
    
ctx.lineWidth = 4;
    
ctx.beginPath();
    
ctx.arc(line.dot.posX, line.dot.posY, 6.5, 0, Math.PI*2, true);
    
ctx.stroke();
    
ctx.globalAlpha = 1;
  },
  
showTT: function(dotX, dotY, text) {
    var 
xy = getXY(this.zinLayout);
    
xy[0] += dotX - 9 + (this.params.bar_chart ? this.barsWidth / 2 : 0);
    
xy[1] += dotY;
    
xy[0] = Math.floor(xy[0]); xy[1] = Math.floor(xy[1]);
    if (
this.mainLayout.tt && this.mainLayout.tt.container && (!isVisible(this.mainLayout.tt.container) || !this.dotLabelXY || this.dotLabelXY[0] != xy[0])) {
      
geByClass1('tt_text', this.mainLayout.tt.container).innerHTML = text;
      
tooltips.hide(this.mainLayout, {fasthide: 1});
      
tooltips.show(this.mainLayout, {forcexy: xy});
      
this.dotLabelXY = xy;
    }
  },
  
onClickMenu: function(ev) {
    var 
g = ev.data.graph;
    for (var 
i in g.menu.children) {
      if (
g.menu.children[i].nodeType != 1) continue;

      if (
i == ev.data.clickNode) {
        
addClass(g.menu.children[i], 'graph_menu_item_sel');
        if (!
ev.data.alt_graph) {
          
g.loadGraph(i);
          if (
isVisible(g.graphDiv.id.replace(/_graph$/, '') + '_alt_graph')) {
            
hide(g.graphDiv.id.replace(/_graph$/, '') + '_alt_graph');
            for (
i in g.mainLayout.children) {
              if (
g.mainLayout.children[i].nodeType == 1 && g.mainLayout.children[i].mh) {
                
show(g.mainLayout.children[i]);
              }
            }
            
g.mainLayout.style.width = g.viewWidth + "px";
            
show(g.vScale);
          }
        } else {
          
hide(g.vScale);
          for (
i in g.mainLayout.children) {
            if (
g.mainLayout.children[i].nodeType != 1 || g.mainLayout.children[i] == g.menu || !isVisible(g.mainLayout.children[i])) continue;
            
g.mainLayout.children[i].mh = 1;
            
hide(g.mainLayout.children[i]);
          }
          
g.mainLayout.style.width = g.viewWidth + g.scaleWidth + "px";
          
show(g.graphDiv.id.replace(/_graph$/, '') + '_alt_graph');
        }
      } else {
        
removeClass(g.menu.children[i], 'graph_menu_item_sel');
      }
    }
    
cancelEvent(ev);
  },

  
// some lines were shown or hidden
  
updateLines: function() {
    
this.redrawWindow();
    
// redraw zoutView
    
var ctx = getContext(this.zoutView);
    
ctx.clearRect(0, 0, this.viewWidth, 40);
    if (
this.isNegative)
      
drawLines(ctx, 0, 4, this.viewWidth, 32, this.lines, this.minTime, this.maxTime, this.xfactorOut, this.yfactorOut, -this.localBottom, true, null, false);
    else
      
drawLines(ctx, 0, 4, this.viewWidth, 36, this.lines, this.minTime, this.maxTime, this.xfactorOut, this.yfactorOut, 0, true, null, false);
  },
  
// window borders were moved
  
redrawWindow: function() {
    
// move window
    
this.xfactorOut = this.viewWidth / (this.maxTime - this.minTime);
    
this.xfactorIn = this.viewWidth / (this.rightTime - this.leftTime);
    var 
leftOut = Math.round((this.leftTime - this.minTime) * this.xfactorOut),
      
rightOut = Math.round((this.rightTime - this.minTime) * this.xfactorOut) - 1;
    
leftOut = Math.min(leftOut, this.viewWidth - 4);
    
rightOut = Math.max(leftOut + 1, rightOut);

    
setStyle(this.lMask, {
      
width: leftOut + "px",
      
display: (leftOut > 0 ? "block" : "none")});
    
setStyle(this.zoutWindow, {
      
left: leftOut + "px",
      
width: (rightOut - leftOut - 1) + "px"});
    
setStyle(this.rMask, {
      
left: (rightOut + 1) + "px",
      
width: (this.viewWidth - rightOut - 2) + "px",
      
display: (this.viewWidth - rightOut - 2 > 0 ? "block" : "none")});

    
this.lHandle.style.left = Math.max(0, leftOut - 4) + "px";
    
this.rHandle.style.left = Math.min(this.viewWidth - 8, rightOut - 4) + "px";

    
// get visible data
    
var localMax = (this.adjust ? -1e9 : 0), localMin = (this.adjust ? 1e9 : 0), outMax = 0, outMin = 0, dotsVisible = 0;
    
this.localLeft = this.rightTime;
    
this.localRight = this.leftTime;
    for (var 
l = 0; l < this.lines.length; l++) {
      var 
line = this.lines[l];
      if (
line.shown) {
        var 
stIdx = 0, enIdx = line.d.length - 1, ps = line.d;
        while ((
stIdx < ps.length - 1) && (ps[stIdx + 1].x < this.leftTime)) stIdx++;
        while ((
enIdx > 1) && (ps[enIdx - 1].x > this.rightTime)) enIdx--;
        
line.stIdx = stIdx;
        
line.enIdx = enIdx;
        if (
enIdx >= stIdx && (ps[enIdx].x >= this.leftTime) && (ps[stIdx].x <= this.rightTime)) {
          for (var 
i = stIdx; i <= enIdx; i++) {
            
localMax = Math.max(localMax, ps[i].y);
            
localMin = Math.min(localMin, ps[i].y);
            
this.localLeft = Math.min(this.localLeft, ps[i].x);
            
this.localRight = Math.max(this.localRight, ps[i].x);
          }
          
dotsVisible = Math.max(dotsVisible, enIdx - stIdx);
        }

        
outMax = Math.max(outMax, line.maxValue);
        
outMin = Math.min(outMax, line.minValue);
      }
    }

    if (
this.lines.length == 0 || (localMax - localMin < 1e-6)) {
      
this.isNegative = false;
      
this.ystep = 1;
      
this.localTop = 5;
      
this.localBottom = 0;
    } else {
      
this.isNegative = (localMin < -1e-6);
      
this.ystep = Math.max(getYStep(localMax - (this.adjust ? localMin : 0), this.params.int_scale), this.isNegative ? getYStep(-localMin, this.params.int_scale) : 1e-6);
      
this.localTop = (Math.ceil(localMax / this.ystep)) * this.ystep;
      
this.localBottom = -(Math.ceil(- localMin / this.ystep)) * this.ystep;
    }
    
this.yfactorIn = (this.viewHeight - (this.isNegative ? 43 : 34)) / (this.localTop - this.localBottom);
    
this.yfactorOut = this.minValue < -1e-6 ? (32 / (outMax - outMin)): (36 / outMax);
    
this.smoothLines = dotsVisible < 30 ? true : false;

    
// update title
    
this.title.innerHTML = fullDate(this.localLeft) + (this.localRight > this.localLeft ? ' &ndash; ' + fullDate(this.localRight) : '');

    
// redraw zinView
    
var ctx = getContext(this.zinView), ctxS = getContext(this.vScaleView);
    
ctx.clearRect(0, 0, this.viewWidth, this.viewHeight);
    
ctx.fillStyle = '#fafafa';
    
ctx.fillRect(0, 9, this.viewWidth, this.viewHeight - 9);
    
ctxS.clearRect(0, 0, this.scaleWidth, this.viewHeight);

    
// y scale
    
ctx.lineWidth = 1;
    
ctx.strokeStyle = '#e6eaf0';
    
ctxS.font = '11px tahoma, arial, verdana, sans-serif, Lucida Sans';
    
ctxS.fillStyle = '#36638e';
    
ctxS.textAlign = 'right';
    
ctxS.textBaseline = 'middle';
    for (var 
gridY = this.localBottom; gridY < this.localTop + this.ystep; gridY += this.ystep) {
      var 
cy = this.viewHeight - 24.5 - Math.round((gridY - this.localBottom) * this.yfactorIn) - (this.isNegative ? 9 : 0);
      if (
gridY < this.localTop) {
        
ctx.strokeStyle = (this.isNegative && Math.abs(gridY) < 1e-8) ? '#9fb1c4' : '#e6eaf0';
        
ctx.beginPath();
        
ctx.moveTo(0, cy);
        
ctx.lineTo(this.viewWidth, cy);
        
ctx.stroke();
      }
      
ctxS.fillText(formatValue(gridY), this.scaleWidth - 6, cy);
    }

    
// x scale
    
ctx.fillStyle = '#ffffff';
    
ctx.fillRect(0, this.viewHeight - 25, this.viewWidth, 25);

    
ctx.font = '11px tahoma, arial, verdana, sans-serif, Lucida Sans';
    
ctx.fillStyle = '#36638e';
    
ctx.textAlign = 'center';
    
ctx.textBaseline = 'top';

    var 
fin = false;
    var 
i_start = this.params.show_time || this.params.show_minutes ? 0 : 6;
    for (var 
i = i_start; (i < 100) && !fin; i++) {
      if (
i < xscales.length) {
        var 
sc = xscales[i];
      } else {
        var 
yearCnt = [2, 5, 10][(i - xscales.length + 1) % 3] * Math.pow(10, Math.floor((i - xscales.length + 1) / 3));
        var 
sc = {base: yearCnt * YEAR, format: ['yearFull'], sub: Math.max(1, yearCnt / 10) * YEAR};
      }

      var 
baseWidth = sc.base * this.xfactorIn;
      for (var 
j = 0; j < sc.format.length; j++) {
        var 
formatWidth = ctx.measureText(formatDate(-1, sc.format[j])).width;
        if (
formatWidth < baseWidth) {
          var 
sTm = incDate(this.leftTime, sc.base, -2);
          var 
eTm = incDate(this.rightTime, sc.base, 2);
          
ctx.strokeStyle = '#e6eaf0';
          if (
sc.sub != -1)
            for (var 
tm = sTm; tm <= eTm; tm = incDate(tm, sc.sub, 1)) {
              var 
ax = Math.floor((tm - this.leftTime) * this.xfactorIn) + 0.5;
              
ctx.beginPath();
              
ctx.moveTo(ax, this.viewHeight - 24);
              
ctx.lineTo(ax, this.viewHeight - 22);
              
ctx.stroke();
            }
          for (var 
tm = sTm; tm <= eTm; tm = incDate(tm, sc.base, 1)) {
            if (
incDate(tm, sc.base, 1) < tm + HOUR)
              throw 
"Erroneous date increase";
            var 
ax = Math.floor((tm - this.leftTime) * this.xfactorIn) + 0.5;
            
//ctx.strokeStyle = '#e6eaf0';
            
ctx.beginPath();
            
ctx.moveTo(ax, 9);
            
ctx.lineTo(ax, this.viewHeight - 24);
            
ctx.stroke();
            
//ctx.strokeStyle = '#9fb1c4';
            
ctx.beginPath();
            
ctx.moveTo(ax, this.viewHeight - 24);
            if (
sc.sub == -1)
              
ctx.lineTo(ax, this.viewHeight);
            else
              
ctx.lineTo(ax, this.viewHeight - 20);
            
ctx.stroke();
            var 
textpos = ax;
            if (
sc.sub == -1)
              
textpos += (baseWidth / 2.0);
            if (
textpos - formatWidth / 2.0 > 0 && textpos + formatWidth / 2.0 < this.viewWidth)
              
ctx.fillText(replaceEntities(formatDate(tm, sc.format[j])), textpos, this.viewHeight - 19);
          }

          
fin = true;
          break;
        }
      }
      if (
fin) break;
    }

    
ctx.strokeStyle = '#e6eaf0';
    
ctx.lineWidth = 1;
    
ctx.strokeRect(0, this.viewHeight - 24.5, this.viewWidth - 1, 26);
    
ctx.strokeStyle = '#f0f2f5';
    
ctx.globalAlpha = 0.5;
    
ctx.beginPath();
    
ctx.moveTo(1, this.viewHeight - 25.5);
    
ctx.lineTo(this.viewWidth, this.viewHeight - 25.5);
    
ctx.stroke();
    
ctx.globalAlpha = 1;

    
// graph
    
if (this.isNegative)
      
drawLines(ctx, 0, 9, this.viewWidth, this.viewHeight - 43, this.lines, this.leftTime, this.rightTime, this.xfactorIn, this.yfactorIn, -this.localBottom, false, this.activeLine, this.smoothLines);
    else
      
drawLines(ctx, 0, 9, this.viewWidth, this.viewHeight - 34, this.lines, this.leftTime, this.rightTime, this.xfactorIn, this.yfactorIn, this.adjust ? -this.localBottom : 0, false, this.activeLine, this.smoothLines);

    
ctx.strokeStyle = '#b8c5d4';
    
ctx.beginPath();
    
ctx.moveTo(0, 9);
    
ctx.lineTo(0, this.viewHeight + 0.5);
    if (!
isVisible(this.zoutLayout)) {
      
ctx.lineTo(this.viewWidth, this.viewHeight + 0.5);
    }
    
ctx.stroke();
  },
  
destroyDots: function(data) {
    for (var 
l = 0; l < this.lines.length; l++) {
      var 
line = this.lines[l];
      
removeEventEx(line.dotLabel, 'mouseover', this.expandLabel);
      
removeEventEx(line.dotLabel, 'mousemove', this.chideLabel);
      
removeEventEx(line.dotLabel, 'mouseout', this.hideLabel);
      
removeEventEx(line.dot, 'click', this.selectLine);
      
removeEventEx(line.legend, 'mouseover', this.overLegend);
      
removeEventEx(line.legend, 'mouseout', this.outLegend);
      
removeEventEx(line.legend, 'click', this.toggleLegend);
      
removeEventEx(line.legend, 'dblclick', this.chooseLegend);
      if (
line.dot) {
        
this.zinLayout.removeChild(line.dot);
      }
      if (
line.legend.parentNode) {
        
line.legend.parentNode.removeChild(line.legend);
      }
      
removeData(line.dotLabel);
      
removeData(line.dot);
      
removeData(line.legend);
    }
  },
  
initLoadedData: function(text) {
    var 
_t = this;
    
this.message.innerHTML = cur.graphVars['lang.data_empty'];
    
setStyle(this.message, {
        
display: 'none',
        
backgroundColor: '#eeeeee',
        
border: '1px solid #5f7d9d'});
    try {
      var 
d = eval(text);
      
_t.loadGraph = function(index) {
        
_t.setData(d[index] || [], isArray(_t.params.adjust) ? _t.params.adjust[index] : _t.params.adjust);
      }
      
_t.noData = (d.length == 0 || (d.length == 1 && d[0].length == 0));
      
_t.setData(_t.params.multiple ? d[0] : d, isArray(_t.params.adjust) ? _t.params.adjust[0] : _t.params.adjust);
    } catch(
e){
      
_t.setData([], false);
    }
  },
  
setData: function(data, adjust) {
    var 
_t = this;
    if (
typeof(data) == 'string') {
      
this.zoutLayout.style.display = 'none';
      
this.vScale.style.display = 'none';
      
this.title.style.display = 'none';
      
//this.message.innerHTML = cur.graphVars['lang.loading'];
      
this.message.innerHTML = '<img src="/images/progress7.gif" />';
      
setStyle(this.message, {
        
display: 'block',
        
backgroundColor: 'transparent',
        
border: 'none',
        
left: Math.round((this.viewWidth - 149) / 2) + 'px',
        
top: Math.round((this.viewHeight - 8) / 2) + 'px'});
      if (
window.ajax) {
        
ajax.post(data, {html5: 1}, {onDone: function(text) {
          
_t.initLoadedData(text);
        }});
      } else {
        
Ajax.Post({url:data.split('%26').join('&'), query: {html5: 1}, onDone: function(res, text) {
          
_t.initLoadedData(text);
        }});
      }
      return;
    }

    if (
this.params.bar_chart) {
      
this.setBarChartData(data, adjust);
      return;
    }

    if (
this.lines.length == data.length) {
      var 
showOverride = [];
      for (var 
l = 0; l < this.lines.length; l++)
        if (
this.lines[l].name.toLowerCase() != data[l].name.toLowerCase()) {
          
showOverride = null;
          break;
        } else
          
showOverride[l] = this.lines[l].shown;
    }

    
// remove old lines
    
this.destroyDots();
    
this.adjust = adjust;
    
this.minValue = 100;
    
this.maxValue = 1;
    
this.minAbsValue = 1.0;
    
this.minTime = 2147483647;
    
this.maxTime = 0;
    
// prepare lines
    
this.lines = [];
    var 
maxDotsCount = 0;
    var 
max_coltext = [0, 0];
    var 
dotsVisible = 0;

    
this.MAXSCALE = 2 * DAY + HOUR;
    
this.XSTEP = DAY;
    for (var 
l = 0; l < data.length; l++) {
      var 
line = extend({a: true}, data[l]);
      if (!
line.d || !line.d.length) {
        continue;
      }
      if (!
line.c || 1) {
        
line.c = colors[l % colors.length];
      }
      var 
r = (line.c & 0xFF0000) >> 16;
      var 
g = (line.c & 0x00FF00) >> 8;
      var 
b = (line.c & 0x0000FF);
      
r = Math.min(255, (r >> 2) + 192);
      
g = Math.min(255, (g >> 2) + 192);
      
b = Math.min(255, (b >> 2) + 192);
      
line.c2 = (r << 16) | (g << 8) | (b);
      
line.hexColor = line.c.toString(16);
      
line.hexColor = '#' + '000000'.substr(0, 6 - line.hexColor.length) + line.hexColor;
      
line.hexColor2 = line.c2.toString(16);
      
line.hexColor2 = '#' + '000000'.substr(0, 6 - line.hexColor.length) + line.hexColor2;
      
line.minValue = 100;
      
line.maxValue = 1;

      
// make dot
      
line.dot = ce('div', {
        
innerHTML: '<div style="color: white;
            -webkit-border-radius: 2px; -khtml-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px;
            background-color: ' 
+ line.hexColor + '; position: absolute; padding: 2px 3px 3px 3px; white-space: nowrap; cursor: default;"></div>'
      
}, {
        
position: 'absolute',
        
display: 'none'
      
});
      
line.dotLabel = line.dot.children[0];
      
this.zinLayout.appendChild(line.dot);

      
addEventEx(this, line.dotLabel, 'mouseover', this.expandLabel);
      
addEventEx(this, line.dotLabel, 'mousemove', this.chideLabel);
      
addEventEx(this, line.dotLabel, 'mouseout', this.hideLabel);
      
addEventEx(this, line.dotLabel, 'mousedown', cancelEvent);
      
//addEventEx(this, line.dotLabel, 'click', this.selectLine);
      //addEventEx(this, line.dot, 'click', this.selectLine);
      
line.dotLabel.line = line;
      
line.dot.line = line;

      
// make legend
      
var col_width = Math.floor(this.viewWidth / 2);

      
line.legend = ce('div', {
        
innerHTML: '<canvas width="20" height="20" style="vertical-align: middle; padding-right: 2px;"></canvas>' +
          
'<span>' + ((line.name.length >= 30) ? line.name.substr(0, 27) + '...' : line.name)  + '</span>' +
          
'<div style="position: absolute; top: 0px; left: 0px; padding: 2px 3px; white-space: nowrap; display: none; background: ' + line.hexColor + '; color: white; -webkit-border-radius: 2px; -khtml-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px;"></div>'
      
}, {
        
color: line.hexColor,
        
position: 'relative',
        
cursor: 'pointer'
      
});
      
line.shown = showOverride ? showOverride[l] : line.a;
      
line.legendCheck = line.legend.children[0];
      
drawCheck(line.legendCheck, line.hexColor, line.hexColor2, line.shown, false);
      
line.legendBox = line.legend.children[2];
      
addEventEx(this, line.legend, 'mouseover', this.overLegend);
      
addEventEx(this, line.legend, 'mouseout', this.outLegend);
      
addEventEx(this, line.legend, 'click', this.toggleLegend);
      
addEventEx(this, line.legend, 'dblclick', this.chooseLegend);
      
line.legend.line = line;

      for (var 
i = 0; i < line.d.length; i++) {
        
line.d[i].x = parseInt(line.d[i].x || line.d[i]["0"] || DEF_TIME);
        
line.d[i].y = parseFloat(line.d[i].y || line.d[i]["1"] || 0);
        
line.d[i].s = line.d[i].s || line.d[i]["2"] || null;
        
line.d[i].l = line.d[i].l || line.d[i]["3"] || null;
        
line.maxValue = Math.max(line.maxValue, line.d[i].y);
        
line.minValue = Math.min(line.minValue, line.d[i].y);
        
this.maxValue = Math.max(this.maxValue, line.d[i].y);
        
this.minValue = Math.min(this.minValue, line.d[i].y);
        
this.minAbsValue = Math.min(this.minAbsValue, Math.abs(line.d[i].y));
        
this.maxTime = Math.max(this.maxTime, line.d[i].x);
        
this.minTime = Math.min(this.minTime, line.d[i].x);
        if (
i > 0 && Math.abs(line.d[i].x - line.d[i - 1].x) > 1) {
          
this.MAXSCALE = Math.min(this.MAXSCALE, 2.5 * Math.abs(line.d[i].x - line.d[i - 1].x));
          
this.XSTEP = (this.XSTEP + Math.abs(line.d[i].x - line.d[i - 1].x)) / 2;
        }
        if (
this.leftTime != -1 && line.d[i].x >= this.leftTime && line.d[i].x <= this.rightTime) {
          
dotsVisible++;
        }
      }
      
maxDotsCount = Math.max(maxDotsCount, line.d.length);

      
// probably not needed
      
line.d.sort(function(l1, l2) {
        return (
l1.x - l2.x);
      });
      
this.lines.push(line);
    }
    for (var 
i = 0; i < this.lines.length; i++) {
      var 
line = this.lines[i];
      if (
i < this.lines.length / 2) {
        
this.column[0].appendChild(line.legend);
        
max_coltext[0] = Math.max(max_coltext[0], getSize(line.legend.children[1])[0]);
      } else {
        
this.column[1].appendChild(line.legend);
        
max_coltext[1] = Math.max(max_coltext[1], getSize(line.legend.children[1])[0]);
      }
    }
    for (var 
i = 0; i < this.lines.length; i++) {
      if (
i < this.lines.length / 2) {
        
this.lines[i].legendBox.style.left = (max_coltext[0] + 40) + 'px';
      } else {
        
this.lines[i].legendBox.style.left = (max_coltext[1] + 40) + 'px';
      }
    }

    var 
noData = this.noData || (this.lines.length == 0 && !this.params.multiple);

    
this.zoutLayout.style.display = noData ? 'none' : 'block';
    
this.vScale.style.display = noData ? 'none' : 'block';;
    
this.title.style.display = noData ? 'none' : 'block';

    if (
this.lines.length == 0) {
      
this.maxTime = incDate(DEF_TIME, DAY, 0);
      
this.minTime = this.maxTime - 7 * DAY;
    } else {
      
this.maxTime += this.XSTEP / 5;
    }
    if (
this.params.no_zoom) {
      
this.leftTime = this.minTime;
      
this.rightTime = this.maxTime;
      
this.allowZoom(false);
    } else {
      if (
this.maxTime - this.minTime < 4 * DAY && maxDotsCount < 10) {
        
this.minTime -= 2 * DAY;
        
this.maxTime += 2 * DAY;
        
this.allowZoom(false);
      } else {
        
this.allowZoom(true);
      }
      
this.showEmpty(noData);

      if (
this.leftTime == -1 || dotsVisible < 10) {
        var 
st = 10 * DAY;
        if (
this.XSTEP > 3 * MONTH) {
          
st = 2 * YEAR;
        } else if (
this.XSTEP > 10 * DAY) {
          
st = 3 * MONTH;
        }
        
this.leftTime = (this.params.show_all || maxDotsCount < 10) ? this.minTime : Math.max(this.minTime, this.maxTime - st);
        
this.rightTime = this.maxTime;
      } else {
        
this.leftTime = Math.max(this.minTime, Math.min(this.maxTime, this.leftTime));
        
this.rightTime = Math.max(this.minTime, Math.min(this.maxTime, this.rightTime));
      }
    }

    var 
ctx = getContext(this.vScaleView);
    
ctx.font = '11px tahoma, arial, verdana, sans-serif, Lucida Sans';
    var 
maxWidth = Math.max(
      
ctx.measureText(formatValue(this.maxValue + getYStep(this.maxValue, this.params.int_scale))).width,
      
Math.max(  ctx.measureText(formatValue(getYStep(this.minAbsValue, this.params.int_scale))).width,
            
ctx.measureText(formatValue(this.minValue)).width));
    
this.scaleWidth = (this.lines.length == 0) ? 0 : (maxWidth + 25);
    
this.scaleWidth = Math.floor(this.scaleWidth);
    
this.vScale.style.width = this.scaleWidth + "px";
    
this.vScale.style.height = this.height + "px";
    
this.vScaleView.width = this.scaleWidth;
    
this.vScaleView.height = this.height;
    
this.viewWidth = (this.width - this.scaleWidth);

    
this.mainLayout.style.width = this.viewWidth + "px";
    
this.zinView.width = this.viewWidth;
    
this.dotsLayer.width = this.viewWidth;
    
this.zoutView.width = this.viewWidth;
    
this.column[0].style.width = Math.floor(this.viewWidth / 2) + "px";
    
this.column[1].style.width = Math.floor(this.viewWidth / 2) + "px";

    
this.activeLine = null;

    
this.updateLines();
  },
  
setBarChartData: function(data, adjust) {
    
// remove old lines
    
this.destroyDots();
    
this.adjust = adjust;
    
this.minValue = 100;
    
this.maxValue = 1;
    
this.minAbsValue = 1.0;
    
// prepare lines
    
this.lines = [];
    var 
max_coltext = [0, 0];

    for (var 
l = 0; l < data.length; l++) {
      var 
line = extend({a: true}, data[l]);
      if (!
line.d || !line.d.length) {
        continue;
      }
      if (!
line.c) {
        var 
colors_row = l % 4;
        
colors_row = colors_row == 1 ? 3 : colors_row;
        
line.c = colors[(colors_row * 8 + Math.floor(l / 4)) % colors.length];
      }
      var 
r = (line.c & 0xFF0000) >> 16;
      var 
g = (line.c & 0x00FF00) >> 8;
      var 
b = (line.c & 0x0000FF);
      
r2 = Math.min(255, (r >> 2) + 192);
      
g2 = Math.min(255, (g >> 2) + 192);
      
b2 = Math.min(255, (b >> 2) + 192);
      
line.c2 = (r2 << 16) | (g2 << 8) | (b2);
      
r3 = Math.min(255, r - 16);
      
g3 = Math.min(255, g - 16);
      
b3 = Math.min(255, b - 16);
      
line.c3 = (r3 << 16) | (g3 << 8) | (b3);
      
line.hexColor = line.c.toString(16);
      
line.hexColor = '#' + '000000'.substr(0, 6 - line.hexColor.length) + line.hexColor;
      
line.hexColor2 = line.c2.toString(16);
      
line.hexColor2 = '#' + '000000'.substr(0, 6 - line.hexColor2.length) + line.hexColor2;
      
line.hexColor3 = line.c3.toString(16);
      
line.hexColor3 = '#' + '000000'.substr(0, 6 - line.hexColor3.length) + line.hexColor3;
      
line.minValue = 100;
      
line.maxValue = 1;

      
// make legend
      
line.legend = ce('div', {
        
innerHTML: '<canvas width="20" height="20" style="vertical-align: middle; padding-right: 2px;"></canvas>' +
          
'<span style="font-weight: bold;">' + ((line.name.length >= 30) ? line.name.substr(0, 27) + '...' : line.name)  + '</span>' +
          
'<div style="position: absolute; top: 0px; left: 0px; padding: 2px 3px; white-space: nowrap; display: none; background: ' + line.hexColor2 + '; -webkit-border-radius: 2px; -khtml-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px;"></div>'
      
}, {
        
color: line.hexColor,
        
position: 'relative',
        
cursor: 'default',
        
paddingLeft: '10px'
      
});
      
line.shown = true;
      
line.legendCheck = line.legend.children[0];
      
line.legendBox = line.legend.children[2];
      var 
ctx = getContext(line.legendCheck);
      
ctx.fillStyle = ctx.strokeStyle = line.hexColor;
      
ctx.lineWidth = 4;
      
ctx.lineJoin = 'round';
      
ctx.strokeRect(4, 5, 9, 9);
      
ctx.fillRect(4, 5, 9, 9);

      if (
line.sum) {
        
line.legendBox.innerHTML = formatValue(line.sum) + this.params.yunits;
        
show(line.legendBox);
      }

      for (var 
i = 0; i < line.d.length; i++) {
        
line.d[i].x = line.d[i].x || line.d[i]["0"] || i;
        
line.d[i].y = parseFloat(line.d[i].y || line.d[i]["1"] || 0);
        
line.d[i].s = line.d[i].s || line.d[i]["2"] || null;
        
line.d[i].l = line.d[i].l || line.d[i]["3"] || null;
        
line.maxValue = Math.max(line.maxValue, line.d[i].y);
        
line.minValue = Math.min(line.minValue, line.d[i].y);
        
this.maxValue = Math.max(this.maxValue, line.d[i].y);
        
this.minValue = Math.min(this.minValue, line.d[i].y);
        
this.minAbsValue = Math.min(this.minAbsValue, Math.abs(line.d[i].y));
      }
      
line.stInd = 0;
      
line.enIdx = line.d.length;

      
this.lines.push(line);
    }
    for (var 
i = 0; i < this.lines.length; i++) {
      var 
line = this.lines[i];
      if (
i < this.lines.length / 2) {
        
this.column[0].appendChild(line.legend);
        
max_coltext[0] = Math.max(max_coltext[0], getSize(line.legend.children[1])[0]);
      } else {
        
this.column[1].appendChild(line.legend);
        
max_coltext[1] = Math.max(max_coltext[1], getSize(line.legend.children[1])[0]);
      }
    }
    for (var 
i = 0; i < this.lines.length; i++) {
      var 
line = this.lines[i];
      if (
i < this.lines.length / 2) {
        
line.legendBox.style.left = (max_coltext[0] + 40) + 'px';
      } else {
        
line.legendBox.style.left = (max_coltext[1] + 40) + 'px';
      }
    }

    var 
noData = this.noData || (this.lines.length == 0 && !this.params.multiple);

    
this.zoutLayout.style.display = noData ? 'none' : 'block';
    
this.vScale.style.display = noData ? 'none' : 'block';;
    
this.showEmpty(noData);

    
this.zoutLayout.style.display = 'none';
    
this.title.style.display = 'none';
    
this.vScaleView.style.paddingTop = 0;
    
this.viewHeight = this.height - 24;
    
this.zinLayout.style.height = this.viewHeight + "px";
    
this.zinView.height = this.viewHeight;
    
this.dotsLayer.height = this.viewHeight - 25;
    
this.disable_zoom = true;
    if (
this.params.multiple && this.menu.parentNode == this.mainLayout) {
      var 
menu = re(this.menu);
      
setStyle(menu, {position: 'absolute', right: '10px', top: '20px', zIndex: 1000});
      
this.zinLayout.insertBefore(menu, this.zinView);
    }

    var 
ctx = getContext(this.vScaleView);
    
ctx.font = '11px tahoma, arial, verdana, sans-serif, Lucida Sans';
    var 
yUnits = this.params.yunits ? this.params.yunits : '';
    var 
maxWidth = Math.max(
      
ctx.measureText(formatValue(this.maxValue + getYStep(this.maxValue, this.params.int_scale) + yUnits)).width, ctx.measureText(formatValue(getYStep(this.minAbsValue, this.params.int_scale) + yUnits)).width);
    
this.scaleWidth = (this.lines.length == 0) ? 0 : (maxWidth + 30);
    
this.scaleWidth = Math.floor(this.scaleWidth);
    
this.vScale.style.width = this.scaleWidth + "px";
    
this.vScale.style.height = this.height + "px";
    
this.vScaleView.width = this.scaleWidth;
    
this.vScaleView.height = this.height;
    
this.viewWidth = (this.width - this.scaleWidth);

    
this.mainLayout.style.width = this.viewWidth + "px";
    
this.zinView.width = this.viewWidth;
    
this.dotsLayer.width = this.viewWidth;
    
this.column[0].style.width = Math.floor(this.viewWidth / 2) + "px";
    
this.column[1].style.width = Math.floor(this.viewWidth / 2) + "px";

    
this.xstep = Math.floor(this.viewWidth / this.params.bar_chart.length);
    
this.barsWidth = Math.floor(this.xstep / (this.lines.length + 1));
    
this.barsWidth = Math.min(this.barsWidth, 25);

    
// draw window
    
if (this.lines.length == 0 || (this.maxValue - this.minValue < 1e-6)) {
      
this.isNegative = false;
      
this.ystep = 1;
      
this.localTop = 5;
      
this.localBottom = 0;
    } else {
      
this.isNegative = (this.minValue < -1e-6);
      
this.ystep = Math.max(getYStep(this.maxValue - (adjust ? this.minValue : 0), this.params.int_scale), this.isNegative ? getYStep(-this.minValue, this.params.int_scale) : 1e-6);
      
this.localTop = (Math.ceil(this.maxValue / this.ystep)) * this.ystep;
      
this.localBottom = 0;
    }
    
this.yfactorIn = (this.viewHeight - (this.isNegative ? 43 : 34)) / (this.localTop - this.localBottom);
    
this.xfactorIn = 1;

    
// redraw zinView
    
var ctx = getContext(this.zinView), ctxS = getContext(this.vScaleView);
    
ctx.clearRect(0, 0, this.viewWidth, this.viewHeight);
    
ctx.fillStyle = '#fafafa';
    
ctx.fillRect(0, 9, this.viewWidth, this.viewHeight - 9);
    
ctxS.clearRect(0, 0, this.scaleWidth, this.viewHeight);

    
// y scale
    
ctx.lineWidth = 1;
    
ctx.strokeStyle = '#e6eaf0';
    
ctxS.font = '11px tahoma, arial, verdana, sans-serif, Lucida Sans';
    
ctxS.fillStyle = '#36638e';
    
ctxS.textAlign = 'right';
    
ctxS.textBaseline = 'middle';
    for (var 
gridY = this.localBottom; gridY < this.localTop + this.ystep; gridY += this.ystep) {
      var 
cy = this.viewHeight - 24.5 - Math.round((gridY - this.localBottom) * this.yfactorIn) - (this.isNegative ? 9 : 0);
      if (
gridY < this.localTop) {
        
ctx.strokeStyle = (this.isNegative && Math.abs(gridY) < 1e-8) ? '#9fb1c4' : '#e6eaf0';
        
ctx.beginPath();
        
ctx.moveTo(0, cy);
        
ctx.lineTo(this.viewWidth, cy);
        
ctx.stroke();
      }
      
ctxS.fillText(formatValue(gridY) + yUnits, this.scaleWidth - 6, cy);
    }

    
// x scale
    
ctx.fillStyle = '#e9ecf0';
    
ctx.fillRect(0, this.viewHeight - 25, this.viewWidth, 25);

    
ctx.font = '11px tahoma, arial, verdana, sans-serif, Lucida Sans';
    
ctx.fillStyle = '#36638e';
    
ctx.textAlign = 'center';
    
ctx.textBaseline = 'top';

    var 
fontSize = 11;
    for (var 
i = 0; i < this.params.bar_chart.length; i++) {
      while (
ctx.measureText(this.params.bar_chart[i]).width > this.xstep && fontSize >= 8) {
        
fontSize--;
        
ctx.font = fontSize + 'px tahoma, arial, verdana, sans-serif, Lucida Sans';
      }
    }

    for (var 
i = 0; i < this.params.bar_chart.length; i++) {
      var 
text = this.params.bar_chart[i];
      var 
textpos = this.xstep * (i + 0.5);
      
ctx.fillText(replaceEntities(text), textpos, this.viewHeight - 19);
    }

    
// graph
    
if (this.isNegative)
      
drawBars(ctx, 0, 9, this.viewWidth, this.viewHeight - 43, this.lines, this.params.bar_chart, this.barsWidth, this.xstep, this.yfactorIn, -this.localBottom);
    else
      
drawBars(ctx, 0, 9, this.viewWidth, this.viewHeight - 34, this.lines, this.params.bar_chart, this.barsWidth, this.xstep, this.yfactorIn, this.adjust ? -this.localBottom : 0);

    
ctx.strokeStyle = '#b8c5d4';
    
ctx.lineWidth = 1;
    
ctx.beginPath();
    
ctx.moveTo(0.5, 9);
    
ctx.lineTo(0.5, this.viewHeight);
    
ctx.lineTo(this.viewWidth, this.viewHeight);
    
ctx.stroke();

    
ctx.strokeStyle = '#6b93b5';
    
ctx.lineWidth = 2;
    
ctx.globalAlpha = 0.25;
    
ctx.beginPath();
    
ctx.moveTo(1, this.viewHeight - 26);
    
ctx.lineTo(this.viewWidth, this.viewHeight - 26);
    
ctx.stroke();
  },
  
destroy: function() {
    
this.destroyDots();
    
this.endDrag();
    
removeEventEx(this.lHandle, 'mousedown touchstart', this.takeHandle);
    
removeEventEx(this.rHandle, 'mousedown touchstart', this.takeHandle);
    
removeEventEx(this.zoutWindow, 'mousedown touchstart', this.takeWindow);
    
removeEventEx(this.lMask, 'mousedown touchstart', this.takeMask);
    
removeEventEx(this.rMask, 'mousedown touchstart', this.takeMask);
    
removeEventEx(this.zinLayout, 'mousedown touchstart', this.takeView);
    
removeEventEx(this.dotsLayer, 'mousemove', this.showDots);
    
removeEventEx(this.zinLayout, 'mouseout', this.hideDots);
    
removeEventEx(this.zinLayout, 'mousewheel DOMMouseScroll', this.wheelView);
    
removeEventEx(this.zoutLayout, 'mousewheel DOMMouseScroll', this.wheelView);
    
removeEvent(this.column[0], 'selectionstart', cancelEvent);
    
removeEvent(this.column[1], 'selectionstart', cancelEvent);
    
this.graphDiv.innerHTML = '';
  },

  
showBadBrowserBox: function() {
    var 
s = (window.devicePixelRatio >= 2 ? '_2x' : '');
    var 
content = getLang('stats_good_browser_box_msg') + '
    <div id="stats_good_browsers" class="clear_fix">
      <a href="http://www.mozilla-europe.org/" target="_blank" style="background: url(/images/firefox'
+s+'.png) no-repeat 50% 17px;">Mozilla Firefox</a>
      <a href="http://www.google.com/chrome/" target="_blank" style="background: url(/images/chrome'
+s+'.png) no-repeat 50% 17px;">Google Chrome</a>
      <a href="http://www.opera.com/" target="_blank" style="background: url(/images/opera'
+s+'.png) no-repeat 50% 15px;">Opera</a>
      <a href="http://www.apple.com/safari/" target="_blank" style="background: url(/images/safari'
+s+'.png) no-repeat 50% 12px;">Safari</a>
    </div>
    <style>
      #stats_good_browsers {
        height: 136px;
        margin: 10px auto 0 auto;
        width: 480px;
      }
      #stats_good_browsers a {
        float: left;
        height: 20px;
        padding: 106px 0 10px 0;
        width: 120px;
        text-align: center;
        -webkit-border-radius: 4px;
        -khtml-border-radius: 4px;
        -moz-border-radius: 4px;
        border-radius: 4px;
      }
      #stats_good_browsers a:hover {
        text-decoration: none;
        background-color: #edf1f5!important;
      }
    </style>'
;
    
cur.badBrowserBox = MessageBox({width: 540, hideButtons: true, bodyStyle: 'padding: 25px; text-align: center;', hideOnBGClick: true}).content(content).show();
    if (!
window.ajax) {
      var 
b = cur.badBrowserBox.body();
      
hide(geByClass1('box_title_wrap', b.parentNode));
      
hide(geByClass1('box_controls_wrap', b.parentNode));
    }
  }
}

window.Graph = Graph;
addEvent(document, 'touchstart touchmove touchend', function(ev) {
  
globalTouchCount = ev.touches.length;
});
})(
window);

try{
stManager.done('graph.js');}catch(e){}
?>
Онлайн: 2
Реклама