Your IP : 18.219.40.177


Current Path : /usr/local/mgr5/skins/orion/src/
Upload File :
Current File : //usr/local/mgr5/skins/orion/src/rack.js

(function(window) {
  'use strict';
  var doT = window.doT,
      doc = window.document,
      UNIT_BORDER = 2,
      UNIT_HEIGHT = 19,
      HALF_POSITION_SIZE = UNIT_HEIGHT / 2,
      MARGIN_LEFT = 50,
      MNODE_SPACE = 250,
      hash = window.hash;
  var Rack = function(wrapper) {
    this.wrapper = wrapper;
    this.template = doT.template(rackTemplate);
    this.nodes = {};
    this.slots = {};
    this.toSaveElems = {};
    this.selectedElems = [];
    this.mnodes = [];
  };


  Rack.fn = Rack.prototype;

  Rack.fn.setData = function(data) {
    this.data = data.map;
    this.edit = true;
    this.tabId = data.tabId;
    this.wrapperSelector = '#' + this.tabId + '-map';
    this.prepareData();
  };

  Rack.fn.prepareData = function() {
    var views = this.data.views,
      viewsBound = this.data.viewsBound,
      viewsTemp = [];
    if (views && views.length) {
      var l = views.length,
        i = 0;
      while (l--) {
        if (views[l].gradient === 'relative') {
          viewsTemp[i] = { name: views[l].name, min: 0, max: 0};
          i++;
        }
      }
      var elems = this.data.elems;
      if (elems && elems.length) {
        var eL = elems.length, eLt, vL = i, vLt, curValue;
        eLt = eL;
        while (eL--) {
          vLt = vL;
          while (vLt--) {
            curValue = elems[eL][viewsTemp[vLt].name];
            if (curValue) {
              if (eL === (eLt - 1)) {
                viewsTemp[vLt].min = curValue;
                viewsTemp[vLt].max = curValue;
              } else {
                viewsTemp[vLt].max = viewsTemp[vLt].max < curValue ? curValue : viewsTemp[vLt].max;
                viewsTemp[vLt].min = viewsTemp[vLt].min > curValue ? curValue : viewsTemp[vLt].min;
              }
            }
            if (elems[eL].embed) {
              var emL = elems[eL].embed.length;
              while (emL--) {
                curValue = elems[eL].embed[emL][viewsTemp[vLt].name];
                if (curValue) {
                  if (eL === (eLt - 1)) {
                    viewsTemp[vLt].min = curValue;
                    viewsTemp[vLt].max = curValue;
                  } else {
                    viewsTemp[vLt].max = viewsTemp[vLt].max < curValue ? curValue : viewsTemp[vLt].max;
                    viewsTemp[vLt].min = viewsTemp[vLt].min > curValue ? curValue : viewsTemp[vLt].min;
                  }
                }
              }
            }
          }
        }
        while (vL--) {
          viewsBound[viewsTemp[vL].name].boundleft = viewsTemp[vL].min;
          viewsBound[viewsTemp[vL].name].boundright = viewsTemp[vL].max;
        }
        this.data.viewsBound = viewsBound;
      }
    }


  };

  Rack.fn.legend = function() {
    var viewsBounds = this.data.viewsBound,
      bl, br, wrapper = doc.querySelector(this.wrapperSelector),
      ind = 0;
    /* jslint forin:true */
    for (var key in viewsBounds) {
      bl = viewsBounds[key].boundleft - 0;
      br = viewsBounds[key].boundright - 0;
      var step = Math.floor(((br - bl) / 10)),
        html = '<h3 class="b-rack-legend__title">' + this.data.views[ind].localName + '</h3>',
        curValue = bl,
        toTen = step % 10,
        count = 10;
      if (toTen !== 0) {
        if ((curValue % 10) !== 0) {
          curValue -= (curValue % 10);
        }
        toTen = 10 - toTen;
        step = step + toTen;
        count = Math.floor(br / step);
      }

      for (var i = 0; i <= count; i++) {
        html += '<div class="b-rack-legend__item"><span class="b-rack-legend__color" style="background-color: ' + gradientGreenToRed(curValue, bl, br) + ';">' +
          '</span><span class="b-rack-legend__value">' + Math.floor(curValue) + '</span></div>';
        curValue += step;
      }
      var div = doc.createElement('div');
      div.className = 'b-rack-legend b-rack-legend_name_' + key;
      div.innerHTML = html;
      wrapper.appendChild(div);
      ind++;
    }

  };

  Rack.fn.saveData = function() {
    if (typeof this.saveDataCallback === 'function') {
      this.saveDataCallback.apply({}, [this.toSaveElems, this.tabId]);
    }
    this.toSaveElems = {};
    this.hideStatus();
  };

  Rack.fn.onSaveData = function(callback) {
    this.saveDataCallback = callback;
  };

  Rack.fn.onSelect = function(callback) {
    this.selectCb = callback;
  };

  Rack.fn.preSaveData = function(node) {
    if (node !== undefined) {
      this.showStatus();
      this.toSaveElems[node.id] = node;
    }
  };

  Rack.fn.showStatus = function() {
    var status = doc.querySelector(this.wrapperSelector + ' .b-map-status');
    if (status) {
      status.classList.add('b-map-status_active');
    }
  };

  Rack.fn.hideStatus = function() {
    var status = doc.querySelector(this.wrapperSelector + ' .b-map-status');
    if (status) {
      status.classList.remove('b-map-status_active');
    }
  };

  Rack.fn.onDblclick = function(callback) {
    this.dblclickCb = callback;
  };

  Rack.fn.changeView = function(e) {
    if (e.target) {
      var views, l, legend;
      //deactivate view
      if (this.activeView) {
        views = doc.querySelectorAll(this.wrapperSelector + ' .b-node__view_name_' + this.activeView + ',' +
          this.wrapperSelector + ' .b-rack-legend_name_' + this.activeView);
        if (views) {
          l = views.length;
          while (l--) {
            views[l].style.display = 'none';
          }
          doc.querySelector(this.wrapperSelector + ' .b-rack-btn_view_' + this.activeView).classList.remove('b-rack-btn_active');
        }
      }

      //activate view
      var elem = e.target,
        viewName = elem.getAttribute('data-view');
        views = doc.querySelectorAll(this.wrapperSelector + ' .b-node__view_name_' + viewName + ',' +
          this.wrapperSelector + ' .b-rack-legend_name_' + viewName);
      if (this.activeView === viewName) {
        this.activeView = null;
        doc.querySelector(this.wrapperSelector + ' .b-nodes').classList.remove('b-nodes_active-view');
        return;
      }
      if (views) {
        doc.querySelector(this.wrapperSelector + ' .b-nodes').classList.add('b-nodes_active-view');
        l = views.length;
        while (l--) {
          views[l].style.display = 'block';
        }
      }
      elem.classList.add('b-rack-btn_active');
      this.activeView = viewName;
    }
    e.preventDefault();
  };

  Rack.fn.render = function() {
    this.data.gradientGreenToRed = gradientGreenToRed;
    this.data.hash = hash;
    this.data.unitBorder = UNIT_BORDER;
    this.data.unitHeight = UNIT_HEIGHT;
    this.data.tabId = this.tabId;
    var html = this.template(this.data),
        self = this,
        l;
    doc.querySelector(this.wrapperSelector).innerHTML = html;

    setTimeout(function() {
      self.buildNodes();
      doc.querySelector(self.wrapperSelector + ' .b-rack-btn')
          .addEventListener('click', self.saveData.bind(self));
      var rackBtn = doc.querySelectorAll(self.wrapperSelector + ' .b-rack-btn');
      if (rackBtn) {
        l = rackBtn.length;
        while (l--) {
          if (rackBtn[l].getAttribute('href') !== '#save') {
            rackBtn[l].addEventListener('click', self.changeView.bind(self));
          }
        }
      }
    }, 300);
    this.legend();
  };

  Rack.fn.buildNodes = function() {
    var elems = this.data.elems,
        unplaced = this.data.unplaced,
        edit = this.edit,
        l = elems.length,
        elem, node, size;
    while (l--) {
      elem = elems[l];
      size = elem.size - 0;
      for (var i = 0; i < size; i++) {
        this.slots[elem.position - 0 - i] = hash(elem.elid + elem.type);
      }
      node = new Node(elem.elid, edit, this);
      node.setData(elem);
      node.onChange(this.preSaveData.bind(this));
      node.onSelect(this.selectNode.bind(this));
      node.onDblclick(this.dblclickNode.bind(this));
      this.nodes[elem.elid] = node;
      if (elem.embed) {
        this.mnodes.push(node);
      }
    }

    if (unplaced) {
      l = unplaced.length;
      while (l--) {
        elem = unplaced[l];
        node = new Node(elem.elid, edit, this);
        node.presetData(elem);
      }
    }
  };

  var CTRL_KEY = 17;

  Rack.fn.selectNode = function(e, node) {
    var codeKey = e.which || e.keyCode,
      ctrl = (CTRL_KEY === codeKey || e.ctrlKey);
    if (ctrl) {
      this.selectedElems.push(node);
    } else {
      //remove node from selected
      var l = this.selectedElems.length;
      while (l--) {
        this.selectedElems[l].domElem.classList.remove('b-node__elem_selected');
      }
      this.selectedElems = [];
      this.selectedElems.push(node);
    }
    if (typeof this.selectCb === 'function') {
      this.selectCb.apply(window, [this.selectedElems, this.tabId]);
    }
  };

  Rack.fn.dblclickNode = function(e, node) {
    var l = this.selectedElems.length;
    while (l--) {
      this.selectedElems[l].domElem.classList.remove('b-node__elem_selected');
    }
    this.selectedElems = [];
    this.selectedElems.push(node);

    if (typeof this.dblclickCb === 'function') {
      this.dblclickCb.apply(window, [node, this.tabId]);
    }
  };

  /**
   * Constructor of Node
   * @param {String} id
   * @param {String} edit
   * @param {Object} rack
   * @constructor
   */
  var Node = function(id, edit, rack) {
    this.id = id;
    this.edit = edit;
    this.rack = rack;
    this.wrapper = rack.wrapperSelector;
  };

  Node.fn = Node.prototype;

  Node.fn.onChange = function(callback) {
    this.changeCallback = callback;
  };

  Node.fn.onSelect = function(callback) {
    this.selectCb = callback;
  };

  Node.fn.onDblclick = function(callback) {
    this.dblclickCb = callback;
  };
  /**
   * get DOM Element of Node
   */

  Node.fn.getDomNode = function() {
    this.domElemMover = doc.querySelector(this.wrapper + ' .n' + this.id);
    this.domElem = doc.querySelector(this.wrapper + ' .b-node__elem_elid_' +
        this.id);
    if (this.type === 'multinode') {
      this.offset = $(this.domElem).offset();
      this.height = this.size * UNIT_HEIGHT;
      this.domInner = doc.querySelector(this.wrapper + ' .b-node__elem_elid_' +
          this.id + ' .b-node-inner');
    }
    if (this.parent) {
      this.domText = doc.querySelector(this.wrapper + ' .b-node__elem_elid_' +
          this.id + ' .b-node__name_multinode');
    }
  };

  Node.fn.presetData = function(data) {
    this.elid = data.elid;
    this.type = data.type;
    this.id = hash(this.elid + this.type);
    this.parent = true;
    this.unplaced = true;
    this.getDomNode();
    if (this.domElemMover) {
      //onDrag
      this.domElemMover.addEventListener('mousedown', this.hDrag.bind(this));
    }
  };
  /**
   * set data to Node element
   * @param {Object} data
   */
  Node.fn.setData = function(data) {
    this.name = data.name;
    this.elid = data.elid;
    this.position = data.position;
    this.size = data.size;
    this.type = data.type;
    this.id = hash(this.elid + this.type);
    this.state = true;
    if (this.edit && !this.parent) {
      this.getDomNode();
      if (this.domElemMover) {
        //onDrag
        this.domElemMover.addEventListener('mousedown', this.drag.bind(this));
      }
      if (this.domElem) {
        //onSelect
        this.domElem.addEventListener('click', this.select.bind(this));
        this.domElem.addEventListener('dblclick', this.dblclick.bind(this));
      }
    }
    if (data.embed) {
      this.embed = [];
      this.slots = [];
      this.volume = data.volume;
      var l = data.embed.length,
        child;
      while (l--) {
        child = new Node(data.embed[l].elid, this.edit, this.rack);
        child.setData(data.embed[l]);

        child.parent = this;
        child.position = data.embed[l].position;

        child.bindChildEvent();
        this.slots[child.position - 0] = hash(child.elid + child.type);

        this.embed.push(child);
      }
    }
  };

  Node.fn.bindChildEvent = function() {
    this.onChange(this.rack.preSaveData.bind(this.rack));
    this.onSelect(this.rack.selectNode.bind(this.rack));
    this.onDblclick(this.rack.dblclickNode.bind(this.rack));

    if (this.domElem) {
      this.getDomNode();
    }

    if (this.domElem) {
      //onSelect
      this.domElem.addEventListener('click', this.select.bind(this));
      this.domElem.addEventListener('dblclick', this.dblclick.bind(this));
    }

    if (this.domElemMover) {
      //onDrag
      this.domElemMover.addEventListener('mousedown', this.hDrag.bind(this));
    }
  };
  /**
   * Drag handler of Node
   * @param {Object} e
   */
  Node.fn.drag = function(e) {
    if (this.position - 0 !== 0) {
      doc.onmousemove = this.move.bind(this);
      doc.onmouseup = this.drop.bind(this);
    } else {
      doc.onmousemove = this.moveToRack.bind(this);
      doc.onmouseup = this.dropToRack.bind(this);
      this.defX = e.clientX;
    }
    this.defY = e.clientY;
    this.domElem.focus();
    this.bottom = parseFloat(this.domElem.style.bottom);
    this.maxBottom = (this.rack.data.size - this.size) * UNIT_HEIGHT;
    e.preventDefault();
  };

  /**
   * Move handler of Node
   * @param {Object} e
   */
  Node.fn.move = function(e) {
    var shiftY = this.defY - e.clientY,
        df = shiftY % UNIT_HEIGHT,
        newBottom;
    if (df < HALF_POSITION_SIZE) {
      shiftY -= df;
    } else {
      shiftY += UNIT_HEIGHT - df;
    }
    newBottom = this.bottom + shiftY;
    if (newBottom < 0) {
      newBottom = 0;
    } else if (newBottom > this.maxBottom) {
      newBottom = this.maxBottom;
    }
    this.checkFreePosition(newBottom);
    this.domElem.style.bottom = newBottom + 'px';
    e.preventDefault();
  };

  var HALF_WIDTH = 175;
  var DEFAULT_LEFT = 350;
  /**
   * Move to Rack handler of node
   * @param {Object} e
   */
  Node.fn.moveToRack = function(e) {
    var shiftX = this.defX - e.clientX,
        curLeft = DEFAULT_LEFT - shiftX;
    if (curLeft > DEFAULT_LEFT) {
      curLeft = DEFAULT_LEFT;
    }

    this.move.apply(this, [e]);

    if (shiftX > HALF_WIDTH) {
      this.domElem.style.left = 0;
    } else {
      this.domElem.style.left = (curLeft) + 'px';
    }
  };
  /**
   * Drop handler of Node
   * @param {Object} e
   */
  Node.fn.drop = function(e) {
    doc.onmousemove = null;
    doc.onmouseup = null;
    var i;
    if (this.position !== this.currentPosition && this.state) {
      for (i = 0; i < this.size - 0; i++) {
        this.rack.slots[this.position - i] = false;
        this.rack.slots[this.currentPosition - i] = this.id;
      }
      this.position = this.currentPosition;
      if (typeof this.changeCallback === 'function') {
        this.changeCallback.apply(this.rack, [this]);
      }
    } else if (this.position !== this.currentPosition) {
      for (i = 0; i < this.size - 0; i++) {
        this.rack.slots[this.position - i] = false;
      }
      this.position = this.currentPosition;
    }
    this.offset = $(this.domElem).offset();
    e.preventDefault();
  };
  /**
   * Drop to Rack handler of Node
   * @param {Object} e
   */
  Node.fn.dropToRack = function(e) {
    doc.onmousemove = null;
    doc.onmouseup = null;
    if (parseInt(this.domElem.style.left, 10) === 0) {
      this.domElem.classList.remove('b-node__elem_withoutpos');
      this.drop.apply(this, [e]);
    } else {
      this.domElem.style.left = DEFAULT_LEFT + 'px';
      this.domElem.style.bottom = this.bottom + 'px';
    }
  };
  /**
   * check position for free
   * @param {Number} currentBottom
   */
  Node.fn.checkFreePosition = function(currentBottom) {
    var currentPosition = (currentBottom / UNIT_HEIGHT),
        size = this.size - 0,
        nodeInCurrentPosition;
    currentPosition += size;
    for (var i = 0; i < size; i++) {
      nodeInCurrentPosition = this.rack.slots[currentPosition - i];
      if (nodeInCurrentPosition && nodeInCurrentPosition !== this.id) {
        this.state = false;
        this.domElem.classList.add('b-node__elem_state_error');
        break;
      } else {
        this.state = true;
        this.domElem.classList.remove('b-node__elem_state_error');
      }
    }
    this.currentPosition = currentPosition;
  };
  /**
   * horizontal drag handler
   * for multinodes
   * @param {Object} e
   */
  Node.fn.hDrag = function(e) {
    var offset = $(this.domElem).offset();
    //unplaced node
    if (this.unplaced) {
      doc.onmousemove = this.hMoveToNode.bind(this);
      doc.onmouseup = this.hDropToNode.bind(this);
      this.left = this.domElem.offsetLeft;

      this.shiftAX = offset.left - this.left;
    } else {
      doc.onmousemove = this.hMove.bind(this);
      doc.onmouseup = this.hDrop.bind(this);
      this.left = parseFloat(this.domElem.style.left);
      this.lastParent = this.parent;
    }

    this.top = this.domElem.offsetTop;
    this.shiftAY = offset.top - this.top;
    this.mnLen = this.rack.mnodes.length;
    this.mnodes = this.rack.mnodes;
    this.defX = e.clientX;
    this.defY = e.clientY;
    this.defWidth = this.domElem.style.width;
    this.defHeight = this.domElem.style.height;
    this.domElem.focus();
    this.width = parseFloat(this.domElem.style.width);
    this.sWidth = this.width;
    this.maxLeft = (this.parent.volume - 1) * this.width + 50;
    this.height = parseFloat(this.domElem.style.height);
    e.preventDefault();
  };


  /**
   * horizontal move handler
   * for multinodes
   * @param {Object} e
   */
  Node.fn.hMove = function(e) {
    if (this.lastX === e.clientX && this.lastY === e.clientY) {
      return;
    }
    //@TODO check repited position
    var shiftX = this.defX - e.clientX,
        shiftY = this.defY - e.clientY,
        curTop = this.top - shiftY,
        df, findParent,
        newLeft, aTop;
    this.lastX = e.clientX;
    this.lastY = e.clientY;

    if (Math.abs(shiftY) > (this.height / 2)) {
      aTop = curTop + this.shiftAY;
      findParent = findOutParent(this, aTop);
      if (findParent) {
        curTop = findParent.offset.top - this.shiftAY;
        if (this.parent.id !== findParent.id) {
          this.parent = findParent;
          this.pretend();
        }
      }
    } else {
      curTop = 0;
    }

    newLeft = this.left - shiftX;
    if (newLeft < MARGIN_LEFT) {
      newLeft = MARGIN_LEFT;
    } else if (newLeft > this.maxLeft) {
      newLeft = this.maxLeft;
    }
    df = (newLeft - 50) % this.width;

    if (df < this.width / 2) {
      newLeft -= df;
    } else {
      newLeft += (this.width - df);
    }

    this.hCheckFreePosition(newLeft);
    this.domElem.style.left = newLeft + 'px';
    this.domElem.style.top = curTop + 'px';
    e.preventDefault();
  };

  var LEFT_MIN_POS = 720;
  var LEFT_POS_RACK = 400;

  Node.fn.hMoveToNode = function(e) {
    if (this.lastX === e.clientX && this.lastY === e.clientY) {
      return;
    }
    var shiftX = this.defX - e.clientX,
        shiftY = this.defY - e.clientY,
        curLeft = this.left - shiftX,
        curTop = this.top - shiftY,
        parent = false,
        newParent = false,
        findParent;
    this.lastX = e.clientX;
    this.lastY = e.clientY;
    if (curTop < 0) {
      curTop = 0;
    }
    if (curLeft < - LEFT_MIN_POS) {
      curLeft = - LEFT_MIN_POS;
    }
    //detect node
    if (curLeft < - LEFT_POS_RACK) {
      var aTop = curTop + this.shiftAY;
      findParent = findOutParent(this, aTop);
      if (findParent) {
        if (typeof this.parent === 'boolean' ||
            (typeof this.parent !== 'boolean' &&
                this.parent.id !== findParent.id)) {
          newParent = true;
        }
        this.parent = findParent;
        parent = true;
      }
    }
    //pretend/unprend for child
    if (!parent) {
      if (this.pretendState) {
        this.unPretend(true);
        this.parent = true;
      }
    } else if (newParent) {
      this.pretend(true);
    }
    //move node inside parent by step
    if (parent) {
      curTop = this.parent.offset.top - this.shiftAY;
      var df = (shiftX - this.df) % this.width;
      if (df <= (this.width / 2)) {
        shiftX -= df;
      } else {
        shiftX += (this.width - df);
      }
      curLeft = (this.left - shiftX);
      if (curLeft < this.minLeft) {
        curLeft = this.minLeft;
      } else if (curLeft > this.maxLeft) {
        curLeft = this.maxLeft;
      }
      //check free position
      var actLeft = MNODE_SPACE - (this.left - curLeft - this.df) + 50 -
          this.width;
      this.hCheckFreePosition(actLeft);
    }
    this.domElem.style.position = 'absolute';
    this.domElem.style.left = curLeft + 'px';
    this.domElem.style.top = curTop + 'px';
    e.preventDefault();
  };

  function findOutParent(elem, top) {
    var l = elem.mnLen;
    while (l--) {
      if (elem.mnodes[l].offset.top <= top &&
          elem.mnodes[l].offset.top + elem.mnodes[l].height >= top) {
        return elem.mnodes[l];
      }
    }
    return false;
  }

  Node.fn.pretend = function(unplaced) {
    var width = MNODE_SPACE / (this.parent.volume - 0),
        height = this.parent.size * UNIT_HEIGHT - UNIT_BORDER;
    this.domElem.style.width = width + 'px';
    this.domElemMover.style.width = width + 'px';
    this.domElem.style.height = height + 'px';
    this.domText.style.height = (height - 11) + 'px';
    this.domText.style.top = (height - 11) + 'px';
    this.width = width;
    if (unplaced) {
      this.maxLeft = (this.parent.volume - 1) * this.width + 50 +
          (this.parent.offset.left - this.shiftAX);
      this.minLeft = MARGIN_LEFT + this.parent.offset.left - this.shiftAX;
      this.df = this.left - this.maxLeft;
    } else {
      this.dfX = this.sWidth - width;
      this.maxLeft = (this.parent.volume - 1) * this.width + 50;
      this.lastPosition = this.currentPosition;
    }

    this.pretendState = true;
  };

  Node.fn.unPretend = function(unplaced) {
    //@TODO unset not need props
    this.domElem.style.width = this.defWidth;
    this.domElem.style.height = this.defHeight;
    this.domElemMover.style.width = this.defWidth;
    var defHeight = parseFloat(this.defHeight);
    this.domText.style.height = (defHeight - 11) + 'px';
    this.domText.style.top = (defHeight - 11) + 'px';
    this.pretendState = false;
    if (unplaced) {
      this.state = false;
      this.domElem.classList.add('b-node__elem_state_error');
      this.parent = true;
    } else {
      this.domElem.style.top = '0px';
      this.parent = this.lastParent;
    }
  };


  Node.fn.hDrop = function(e) {
    //@TODO unset not need props
    doc.onmousemove = null;
    doc.onmouseup = null;
    if (this.lastParent && this.parent.id !== this.lastParent.id) {
      //free slot in prev parent
      if (this.lastParent.slots[this.currentPosition] === this.id) {
        this.lastParent.slots[this.currentPosition] = false;
      } else if (this.lastParent.slots[this.position] === this.id) {
        this.lastParent.slots[this.position] = false;
      }
      this.adoptionMe();
    } else if (this.pretendState) {
      this.unPretend();
    }
    if (this.position !== this.currentPosition && this.state) {
      this.parent.slots[this.position] = false;
      this.parent.slots[this.currentPosition] = this.id;
      this.position = this.currentPosition;
      if (typeof this.changeCallback === 'function') {
        this.changeCallback.apply(this.parent, [this]);
      }
    } else if (this.position !== this.currentPosition) {
      this.parent.slots[this.position] = false;
      this.position = this.currentPosition;
    }

    //unset not needed params
    this.lastParent = null;
    this.lastPosition = null;
    this.shiftAX = null;
    this.left = null;
    this.top = null;
    this.mnLen = null;
    this.mnodes = null;
    this.defX = null;
    this.defY = null;
    this.defHeight = null;
    this.defWidth = null;
    this.sWidth = null;
    this.maxLeft = null;
    this.height = null;
    this.lastX = null;
    this.lastY = null;
    e.preventDefault();
  };

  Node.fn.hDropToNode = function(e) {
    doc.onmousemove = null;
    doc.onmouseup = null;
    this.domElem.style.position = '';
    this.domElem.style.top = '';
    this.domElem.style.left = '';
    if (this.pretendState) {
      if (this.state) {
        //move element to node parent
        this.adoptionMe(true);
        //apply drop for change event
        this.hDrop.apply(this, [e]);
      } else {
        //return elem to unplaced
        this.unPretend(true);
      }
    }
    e.preventDefault();
  };

  Node.fn.adoptionMe = function(unplaced) {
    this.parent.domInner.appendChild(this.domElem);
    this.domElem.style.left = (this.width * (this.currentPosition - 1) + 50) +
        'px';
    if (unplaced) {
      this.domElem.classList.remove('b-node__multi_elem_unplaced');
      this.unplaced = false;
      this.pretendState = false;
      this.bindChildEvent();
    } else {
      this.domElem.style.top = '0px';
    }
    this.position = 0;
  };

  Node.fn.hCheckFreePosition = function(currentLeft) {
    var currentPosition = Math.round(((currentLeft - MARGIN_LEFT) / this.width)) + 1,
        nodeInCurrentPosition;

      nodeInCurrentPosition = this.parent.slots[currentPosition];
      if (nodeInCurrentPosition && nodeInCurrentPosition !== this.id) {
        this.state = false;
        this.domElem.classList.add('b-node__elem_state_error');
      } else {
        this.state = true;
        this.domElem.classList.remove('b-node__elem_state_error');
      }

    this.currentPosition = currentPosition;
  };

  Node.fn.select = function(e) {
    this.domElem.classList.add('b-node__elem_selected');
    if (typeof this.selectCb === 'function') {
      this.selectCb(e, this);
    }
    if (this.domElem.classList.contains('b-node__multi_elem')) {
      e.stopPropagation();
    }
  };

  Node.fn.dblclick = function(e) {
    this.domElem.classList.add('b-node__elem_selected');
    if (typeof this.dblclickCb === 'function') {
      this.dblclickCb(e, this);
    }
    if (this.domElem.classList.contains('b-node__multi_elem')) {
      e.stopPropagation();
    }
  };

  function gradientGreenToRed(value, min, max) {
    // value is a value between 0 and max;
    // 0 = green, max/2 = yellow, max = red.
    var R, G,
        B = 0,
        bound = max - min,
        halfMax = bound / 2;

    if (value > halfMax) {
      //yellow to red
      R = 255;
      G = Math.round((255 * (bound - value)) / halfMax);
    } else {
      //green to yellow
      G = 255;
      R = Math.round((255 * value) / halfMax);
    }
    return 'rgb(' + R + ',' + G + ',' + B + ')';
  }




  var rackTemplate = ' ' +
    '{{##def.mnode:' +
      '{{ var color = ""; if (mnode.color) { color="background-color:" + mnode.color + ";" } }}' +
      '<div class="b-node__multi_elem acthint b-node__elem_elid_{{=it.hash(mnode.elid + mnode.type)}} {{=extClass}}" tabindex="0" ' +
      'data-elid="{{=mnode.elid}}" data-name="{{=mnode.type}}" ' +
      'data-hintfunc="{{=mnode.hintfunc}}" ' +
      'style="{{=color}}width:{{=width}}px;left: {{=left}}px;height: {{=height}}px;">' +
      '{{~mnode.views :valueM:indexM}}' +
      '<div class="b-node__view b-node__view_name_{{=valueM.n}}" ' +
      'style="width:{{=width}}px;height: {{=height}}px;background-color:{{=it.gradientGreenToRed(valueM.v, it.viewsBound[valueM.n].boundleft, it.viewsBound[valueM .n].boundright)}};"></div>' +
      '{{~}}' +
      '<div class="b-node__name_multinode " style="width: {{=height-11}}px;top:{{=height-11}}px"' +
      ' data-elid="{{=mnode.elid}}">{{=mnode.name}}</div>' +
      '<div class="b-node__props_multinode">' +
      '{{~mnode.props :value1:index1}}' +
      '{{?(value1["value"])}}' +
      '{{ var bg = ""; if (value1["color"] && value1["value"] == "on") { bg = "style= \'background-color: " + value1[\"color\"] + ";\'" } }}' +
      '<div class=\"b-prop-multinode b-prop_{{=value1["name"]}}_{{=value1["value"]}}\" {{=bg}} data-hint=\"{{=value1["hint"]}}\"></div>' +
      '{{?}}' +
      '{{~}}' +
      '</div>' +

      '<div class="b-node-multi__mover n{{=it.hash(mnode.elid + mnode.type)}}" style="width:{{=width}}px"></div>' +
      '</div>' +
    '#}}' +

    '<div class="b-rack-toolbar">' +
    '<a href="#save" class="b-rack-btn b-rack-btn_save" data-id="#">{{=it.msg.save}}</a>' +
    '{{~it.views :value:index}}' +
    '<a href="#{{=value.name}}" class="b-rack-btn b-rack-btn_view_{{=value.name}}" data-view="{{=value.name}}" data-id="#">{{=value.localName}}</a>' +
    '{{~}}' +
    '<span class="b-map-status">{{=it.msg.unsave}}</span>' +
    '</div>' +
    '<div class="b-rack" id="b-rack-{{=it.tabId}}">' +
    '<div class="b-rack__name">{{=it.msg.rack_name}}</div>' +
      '<div class="b-rack__numeric">' +
        '<ul class="b-rack__numeric-list">' +
        '{{ ' +
            'var l = it.size; ' +
            'while (l--) { ' +
        '}}' +
          '<li class="b-rack__numeric-item">{{=l+1}}</li>' +
        '{{ ' +
          '} ' +
        '}}' +
        '</ul>' +
      '</div> ' +
      '<div class="b-nodes">' +
            '<div class="b-node__cont">' +
            '{{ ' +
            'var l = it.elems.length; ' +
            'var windex = 0; ' +
              'while (l--) { ' +
            ' }}' +
                '{{ var height = (it.elems[l].size * it.unitHeight) - it.unitBorder;}}' +
                '{{ var color = ""; if (it.elems[l].color) { color="background-color:" + it.elems[l].color + ";" } }}' +
                '{{ var extClass = ""; if (it.elems[l].position === "0") { extClass="b-node__elem_withoutpos b-node__elem_state_error"; } }}' +
                '{{ var bottom = ""; if (it.elems[l].position === "0") { windex += (it.elems[l].size - 0) ; bottom = (windex - it.elems[l].size) * it.unitHeight; } else { }}' +
                '{{ bottom = (it.elems[l].position - it.elems[l].size) * it.unitHeight; } }}' +
                '<div class="b-node__elem b-node__elem_elid_{{=it.hash(it.elems[l].elid + it.elems[l].type)}} {{=extClass}}" data-elid="{{=it.elems[l].elid}}" tabindex="0" data-pos="{{=it.elems[l].position}}"' +
                  ' style="{{=color}}height: {{=height}}px; bottom: {{=bottom}}px;">' +
                  '{{~it.elems[l].views :valueV:indexV}}' +
                  '<div class="b-node__view b-node__view_name_{{=valueV.n}}" style="height: {{=height}}px;background-color:{{=it.gradientGreenToRed(valueV.v, it.viewsBound[valueV.n].boundleft, it.viewsBound[valueV.n].boundright)}};"></div>' +
                  '{{~}}' +
                  '<div class="b-node-inner  acthint" data-elid="{{=it.elems[l].elid}}" data-name="{{=it.elems[l].type}}" data-hintfunc="{{=it.elems[l].hintfunc}}">' +
                  '<div class="b-node__props" style="height: {{=height}}px;">' +
                    '{{~it.elems[l].props :value:index}}' +
                    '{{ if (value["value"]) { }}' +
                    '{{ var bg = ""; if (value["color"] && value["value"] == "on") { bg = "style= \'background-color: " + value[\"color\"] + ";\'" } }}' +
                      '<div class=\"b-prop  b-prop_{{=value["name"]}}_{{=value["value"]}} \" {{=bg}} data-hint=\"{{=value["hint"]}}\"></div>' +
                    '{{ } }}' +
                    '{{~}}' +
                  '</div>' +
                  '<div class="b-node__name" data-elid="{{=it.elems[l].elid}}">{{=it.elems[l].name}}</div>' +
                  '{{?(it.elems[l].type === \'multinode\')}}' +
                    '{{~it.elems[l].embed :mnode:index}}' +
                    '{{ var width = (250 / it.elems[l].volume), height = (it.elems[l].size * it.unitHeight) - it.unitBorder; }}' +
                    '{{ var extClass = ""; var left = width * (mnode.position - 1) + 50; }}' +

                      '{{#def.mnode}}' +

                    '{{~}}' +
                  '{{?}}' +
                    '</div>' +
                  '<div class="b-node__mover n{{=it.hash(it.elems[l].elid + it.elems[l].type)}}" style="height: {{=(it.elems[l].size * it.unitHeight) - it.unitBorder}}px;"></div>' +
                '</div>' +
              ' {{ ' +
              ' } ' +
            ' }}' +
      '</div>' +
    '</div>' +
    '<div class="b-rack-bottom"></div>' +

    '</div>' +
    '<div class="b-rack-unplaced">' +
      '{{?it.unplaced}}' +
        '<div>' +
        '{{ var extClass = "b-node__multi_elem_unplaced b-node__elem_state_error"; }}' +
        '{{~it.unplaced :mnode:index}}' +
          '{{ var width = (250 / 8), height = (3 * it.unitHeight) - it.unitBorder; }}' +
          '{{ var left = 0; }}' +
          '{{#def.mnode}}' +
        '{{~}}' +
        '</div>' +
      '{{?}}' +
    '</div>';

  window.Rack = Rack;

}(window));