<commit_before>function WalkontableBorder(instance, settings) {
  var style;
  var createMultipleSelectorHandles = function () {
    this.selectionHandles = {
      topLeft: document.createElement('DIV'),
      topLeftHitArea: document.createElement('DIV'),
      bottomRight: document.createElement('DIV'),
      bottomRightHitArea: document.createElement('DIV')
    };
    var width = 10
      , hitAreaWidth = 40;

    this.selectionHandles.topLeft.className = 'topLeftSelectionHandle';
    this.selectionHandles.topLeftHitArea.className = 'topLeftSelectionHandle-HitArea';
    this.selectionHandles.bottomRight.className = 'bottomRightSelectionHandle';
    this.selectionHandles.bottomRightHitArea.className = 'bottomRightSelectionHandle-HitArea';

    this.selectionHandles.styles = {
      topLeft: this.selectionHandles.topLeft.style,
      topLeftHitArea: this.selectionHandles.topLeftHitArea.style,
      bottomRight: this.selectionHandles.bottomRight.style,
      bottomRightHitArea: this.selectionHandles.bottomRightHitArea.style
    };

    var hitAreaStyle = {
      'position': 'absolute',
      'height': hitAreaWidth + 'px',
      'width': hitAreaWidth + 'px',
      'border-radius': parseInt(hitAreaWidth/1.5,10) + 'px'
    };

    for (var prop in hitAreaStyle) {
      if (hitAreaStyle.hasOwnProperty(prop)) {
        this.selectionHandles.styles.bottomRightHitArea[prop] = hitAreaStyle[prop];
        this.selectionHandles.styles.topLeftHitArea[prop] = hitAreaStyle[prop];
      }
    }

    var handleStyle = {
      'position': 'absolute',
      'height': width + 'px',
      'width': width + 'px',
      'border-radius': parseInt(width/1.5,10) + 'px',
      'background': '#F5F5FF',
      'border': '1px solid #4285c8'
    };

    for (var prop in handleStyle) {
      if (handleStyle.hasOwnProperty(prop)) {
        this.selectionHandles.styles.bottomRight[prop] = handleStyle[prop];
        this.selectionHandles.styles.topLeft[prop] = handleStyle[prop];
      }
    }

    this.main.appendChild(this.selectionHandles.topLeft);
    this.main.appendChild(this.selectionHandles.bottomRight);
    this.main.appendChild(this.selectionHandles.topLeftHitArea);
    this.main.appendChild(this.selectionHandles.bottomRightHitArea);
  };

  if(!settings){
    return;
  }

  var eventManager = Handsontable.eventManager(instance);

  //reference to instance
  this.instance = instance;
  this.settings = settings;

  this.main = document.createElement("div");
  style = this.main.style;
  style.position = 'absolute';
  style.top = 0;
  style.left = 0;

  var borderDivs = ['top','left','bottom','right','corner'];

  for (var i = 0; i < 5; i++) {
    var position = borderDivs[i];

    var DIV = document.createElement('DIV');
    DIV.className = 'wtBorder ' + (this.settings.className || ''); // + borderDivs[i];
    if(this.settings[position] && this.settings[position].hide){
      DIV.className += " hidden";
    }

    style = DIV.style;
    style.backgroundColor = (this.settings[position] && this.settings[position].color) ? this.settings[position].color : settings.border.color;
    style.height = (this.settings[position] && this.settings[position].width) ? this.settings[position].width + 'px' : settings.border.width + 'px';
    style.width = (this.settings[position] && this.settings[position].width) ? this.settings[position].width + 'px' : settings.border.width + 'px';

    this.main.appendChild(DIV);
  }

  this.top = this.main.childNodes[0];
  this.left = this.main.childNodes[1];
  this.bottom = this.main.childNodes[2];
  this.right = this.main.childNodes[3];

  this.topStyle = this.top.style;
  this.leftStyle = this.left.style;
  this.bottomStyle = this.bottom.style;
  this.rightStyle = this.right.style;

  this.cornerDefaultStyle = {
    width: '5px',
    height: '5px',
    borderWidth: '2px',
    borderStyle: 'solid',
    borderColor: '#FFF'
  };

  this.corner = this.main.childNodes[4];
  this.corner.className += ' corner';
  this.cornerStyle = this.corner.style;
  this.cornerStyle.width = this.cornerDefaultStyle.width;
  this.cornerStyle.height = this.cornerDefaultStyle.height;
  this.cornerStyle.border = [
    this.cornerDefaultStyle.borderWidth,
    this.cornerDefaultStyle.borderStyle,
    this.cornerDefaultStyle.borderColor
  ].join(' ');

  if(Handsontable.mobileBrowser) {
    createMultipleSelectorHandles.call(this);
  }

  this.disappear();
  if (!instance.wtTable.bordersHolder) {
    instance.wtTable.bordersHolder = document.createElement('div');
    instance.wtTable.bordersHolder.className = 'htBorders';
    instance.wtTable.hider.appendChild(instance.wtTable.bordersHolder);

  }
  instance.wtTable.bordersHolder.insertBefore(this.main, instance.wtTable.bordersHolder.firstChild);

  var down = false;

  eventManager.addEventListener(document.body, 'mousedown', function () {
    down = true;
  });

  eventManager.addEventListener(document.body, 'mouseup', function () {
    down = false;
  });

  /* jshint ignore:start */
  for (var c = 0, len = this.main.childNodes.length; c < len; c++) {

    eventManager.addEventListener(this.main.childNodes[c], 'mouseenter', function (event) {
      if (!down || !instance.getSetting('hideBorderOnMouseDownOver')) {
        return;
      }
      event.preventDefault();
      event.stopImmediatePropagation();

      var bounds = this.getBoundingClientRect();

      this.style.display = 'none';

      var isOutside = function (event) {
        if (event.clientY < Math.floor(bounds.top)) {
          return true;
        }
        if (event.clientY > Math.ceil(bounds.top + bounds.height)) {
          return true;
        }
        if (event.clientX < Math.floor(bounds.left)) {
          return true;
        }
        if (event.clientX > Math.ceil(bounds.left + bounds.width)) {
          return true;
        }
      };

      var handler = function (event) {
        if (isOutside(event)) {
          eventManager.removeEventListener(document.body, 'mousemove', handler);
          this.style.display = 'block';
        }
      };
      eventManager.addEventListener(document.body, 'mousemove', handler);
    });
  }
  /* jshint ignore:end */
}

/**
 * Show border around one or many cells
 * @param {Array} corners
 */
WalkontableBorder.prototype.appear = function (corners) {
  var isMultiple, fromTD, toTD, fromOffset, toOffset, containerOffset, top, minTop, left, minLeft, height, width;
  if (this.disabled) {
    return;
  }

  var instance = this.instance;

  var fromRow
    , fromColumn
    , toRow
    , toColumn
    , i
    , ilen
    , s;

  var isPartRange = function () {
    if(this.instance.selections.area.cellRange) {

      if (toRow != this.instance.selections.area.cellRange.to.row ||
          toColumn != this.instance.selections.area.cellRange.to.col) {
        return true;
      }
    }

    return false;
  };

  var updateMultipleSelectionHandlesPosition = function (top, left, width, height) {
    var handleWidth = parseInt(this.selectionHandles.styles.topLeft.width, 10)
      , hitAreaWidth = parseInt(this.selectionHandles.styles.topLeftHitArea.width, 10);

    this.selectionHandles.styles.topLeft.top = parseInt(top - handleWidth,10) + "px";
    this.selectionHandles.styles.topLeft.left = parseInt(left - handleWidth,10) + "px";

    this.selectionHandles.styles.topLeftHitArea.top = parseInt(top - (hitAreaWidth/4)*3,10) + "px";
    this.selectionHandles.styles.topLeftHitArea.left = parseInt(left - (hitAreaWidth/4)*3,10) + "px";

    this.selectionHandles.styles.bottomRight.top = parseInt(top + height,10) + "px";
    this.selectionHandles.styles.bottomRight.left = parseInt(left + width,10) + "px";

    this.selectionHandles.styles.bottomRightHitArea.top = parseInt(top + height - hitAreaWidth/4,10) + "px";
    this.selectionHandles.styles.bottomRightHitArea.left = parseInt(left + width - hitAreaWidth/4,10) + "px";

    if(this.settings.border.multipleSelectionHandlesVisible && this.settings.border.multipleSelectionHandlesVisible()) {
      this.selectionHandles.styles.topLeft.display = "block";
      this.selectionHandles.styles.topLeftHitArea.display = "block";
      if(!isPartRange.call(this)) {
        this.selectionHandles.styles.bottomRight.display = "block";
        this.selectionHandles.styles.bottomRightHitArea.display = "block";
      } else {
        this.selectionHandles.styles.bottomRight.display = "none";
        this.selectionHandles.styles.bottomRightHitArea.display = "none";
      }
    } else {
      this.selectionHandles.styles.topLeft.display = "none";
      this.selectionHandles.styles.bottomRight.display = "none";
      this.selectionHandles.styles.topLeftHitArea.display = "none";
      this.selectionHandles.styles.bottomRightHitArea.display = "none";
    }

    if(fromRow == this.instance.wtSettings.getSetting('fixedRowsTop') || fromColumn == this.instance.wtSettings.getSetting('fixedColumnsLeft')) {
      this.selectionHandles.styles.topLeft.zIndex = "9999";
      this.selectionHandles.styles.topLeftHitArea.zIndex = "9999";
    } else {
      this.selectionHandles.styles.topLeft.zIndex = "";
      this.selectionHandles.styles.topLeftHitArea.zIndex = "";
    }

  };

  if (instance.cloneOverlay instanceof WalkontableVerticalScrollbarNative || instance.cloneOverlay instanceof WalkontableCornerScrollbarNative) {
    ilen = instance.getSetting('fixedRowsTop');
  }
  else {
    ilen = instance.wtTable.getRenderedRowsCount();
  }

  for (i = 0; i < ilen; i++) {
    s = instance.wtTable.rowFilter.renderedToSource(i);
    if (s >= corners[0] && s <= corners[2]) {
      fromRow = s;
      break;
    }
  }

  for (i = ilen - 1; i >= 0; i--) {
    s = instance.wtTable.rowFilter.renderedToSource(i);
    if (s >= corners[0] && s <= corners[2]) {
      toRow = s;
      break;
    }
  }

  ilen = instance.wtTable.getRenderedColumnsCount();

  for (i = 0; i < ilen; i++) {
    s = instance.wtTable.columnFilter.renderedToSource(i);
    if (s >= corners[1] && s <= corners[3]) {
      fromColumn = s;
      break;
    }
  }

  for (i = ilen - 1; i >= 0; i--) {
    s = instance.wtTable.columnFilter.renderedToSource(i);
    if (s >= corners[1] && s <= corners[3]) {
      toColumn = s;
      break;
    }
  }

  if (fromRow !== void 0 && fromColumn !== void 0) {
    isMultiple = (fromRow !== toRow || fromColumn !== toColumn);
    fromTD = instance.wtTable.getCell(new WalkontableCellCoords(fromRow, fromColumn));
    toTD = isMultiple ? instance.wtTable.getCell(new WalkontableCellCoords(toRow, toColumn)) : fromTD;
    fromOffset = Handsontable.Dom.offset(fromTD);
    toOffset = isMultiple ? Handsontable.Dom.offset(toTD) : fromOffset;
    containerOffset = Handsontable.Dom.offset(instance.wtTable.TABLE);

    minTop = fromOffset.top;
    height = toOffset.top + Handsontable.Dom.outerHeight(toTD) - minTop;
    minLeft = fromOffset.left;
    width = toOffset.left + Handsontable.Dom.outerWidth(toTD) - minLeft;

    top = minTop - containerOffset.top - 1;
    left = minLeft - containerOffset.left - 1;

    var style = Handsontable.Dom.getComputedStyle(fromTD);
    if (parseInt(style['borderTopWidth'], 10) > 0) {
      top += 1;
      height = height > 0 ? height - 1 : 0;
    }
    if (parseInt(style['borderLeftWidth'], 10) > 0) {
      left += 1;
      width = width > 0 ? width - 1 : 0;
    }
  }
  else {
    this.disappear();
    return;
  }

  this.topStyle.top = top + 'px';
  this.topStyle.left = left + 'px';
  this.topStyle.width = width + 'px';
  this.topStyle.display = 'block';

  this.leftStyle.top = top + 'px';
  this.leftStyle.left = left + 'px';
  this.leftStyle.height = height + 'px';
  this.leftStyle.display = 'block';

  var delta = Math.floor(this.settings.border.width / 2);

  this.bottomStyle.top = top + height - delta + 'px';
  this.bottomStyle.left = left + 'px';
  this.bottomStyle.width = width + 'px';
  this.bottomStyle.display = 'block';

  this.rightStyle.top = top + 'px';
  this.rightStyle.left = left + width - delta + 'px';
  this.rightStyle.height = height + 1 + 'px';
  this.rightStyle.display = 'block';

  if (Handsontable.mobileBrowser || (!this.hasSetting(this.settings.border.cornerVisible) || isPartRange.call(this))) {
    this.cornerStyle.display = 'none';
  }
  else {
    this.cornerStyle.top = top + height - 4 + 'px';
    this.cornerStyle.left = left + width - 4 + 'px';
    this.cornerStyle.borderRightWidth = this.cornerDefaultStyle.borderWidth;
    this.cornerStyle.width = this.cornerDefaultStyle.width;
    this.cornerStyle.display = 'block';

    if (toColumn === this.instance.getSetting('totalColumns') - 1) {
      var scrollableElement = Handsontable.Dom.getScrollableElement(instance.wtTable.TABLE),
        cornerOverlappingContainer = toTD.offsetLeft + Handsontable.Dom.outerWidth(toTD) >= Handsontable.Dom.innerWidth(scrollableElement);
      if (cornerOverlappingContainer) {
        this.cornerStyle.left = Math.floor(left + width - 3 - parseInt(this.cornerDefaultStyle.width) / 2) + "px";
        this.cornerStyle.borderRightWidth = 0;
      }
    }
  }

  if (Handsontable.mobileBrowser) {
    updateMultipleSelectionHandlesPosition.call(this, top, left, width, height);
  }
};

/**
 * Hide border
 */
WalkontableBorder.prototype.disappear = function () {
  this.topStyle.display = 'none';
  this.leftStyle.display = 'none';
  this.bottomStyle.display = 'none';
  this.rightStyle.display = 'none';
  this.cornerStyle.display = 'none';

  if(Handsontable.mobileBrowser) {
    this.selectionHandles.styles.topLeft.display = 'none';
    this.selectionHandles.styles.bottomRight.display = 'none';
  }

};

WalkontableBorder.prototype.hasSetting = function (setting) {
  if (typeof setting === 'function') {
    return setting();
  }
  return !!setting;
};
<commit_msg>Add an empty line after variable declaration #2085<commit_after>function WalkontableBorder(instance, settings) {
  var style;
  var createMultipleSelectorHandles = function () {
    this.selectionHandles = {
      topLeft: document.createElement('DIV'),
      topLeftHitArea: document.createElement('DIV'),
      bottomRight: document.createElement('DIV'),
      bottomRightHitArea: document.createElement('DIV')
    };
    var width = 10
      , hitAreaWidth = 40;

    this.selectionHandles.topLeft.className = 'topLeftSelectionHandle';
    this.selectionHandles.topLeftHitArea.className = 'topLeftSelectionHandle-HitArea';
    this.selectionHandles.bottomRight.className = 'bottomRightSelectionHandle';
    this.selectionHandles.bottomRightHitArea.className = 'bottomRightSelectionHandle-HitArea';

    this.selectionHandles.styles = {
      topLeft: this.selectionHandles.topLeft.style,
      topLeftHitArea: this.selectionHandles.topLeftHitArea.style,
      bottomRight: this.selectionHandles.bottomRight.style,
      bottomRightHitArea: this.selectionHandles.bottomRightHitArea.style
    };

    var hitAreaStyle = {
      'position': 'absolute',
      'height': hitAreaWidth + 'px',
      'width': hitAreaWidth + 'px',
      'border-radius': parseInt(hitAreaWidth/1.5,10) + 'px'
    };

    for (var prop in hitAreaStyle) {
      if (hitAreaStyle.hasOwnProperty(prop)) {
        this.selectionHandles.styles.bottomRightHitArea[prop] = hitAreaStyle[prop];
        this.selectionHandles.styles.topLeftHitArea[prop] = hitAreaStyle[prop];
      }
    }

    var handleStyle = {
      'position': 'absolute',
      'height': width + 'px',
      'width': width + 'px',
      'border-radius': parseInt(width/1.5,10) + 'px',
      'background': '#F5F5FF',
      'border': '1px solid #4285c8'
    };

    for (var prop in handleStyle) {
      if (handleStyle.hasOwnProperty(prop)) {
        this.selectionHandles.styles.bottomRight[prop] = handleStyle[prop];
        this.selectionHandles.styles.topLeft[prop] = handleStyle[prop];
      }
    }

    this.main.appendChild(this.selectionHandles.topLeft);
    this.main.appendChild(this.selectionHandles.bottomRight);
    this.main.appendChild(this.selectionHandles.topLeftHitArea);
    this.main.appendChild(this.selectionHandles.bottomRightHitArea);
  };

  if(!settings){
    return;
  }

  var eventManager = Handsontable.eventManager(instance);

  //reference to instance
  this.instance = instance;
  this.settings = settings;

  this.main = document.createElement("div");
  style = this.main.style;
  style.position = 'absolute';
  style.top = 0;
  style.left = 0;

  var borderDivs = ['top','left','bottom','right','corner'];

  for (var i = 0; i < 5; i++) {
    var position = borderDivs[i];

    var DIV = document.createElement('DIV');
    DIV.className = 'wtBorder ' + (this.settings.className || ''); // + borderDivs[i];
    if(this.settings[position] && this.settings[position].hide){
      DIV.className += " hidden";
    }

    style = DIV.style;
    style.backgroundColor = (this.settings[position] && this.settings[position].color) ? this.settings[position].color : settings.border.color;
    style.height = (this.settings[position] && this.settings[position].width) ? this.settings[position].width + 'px' : settings.border.width + 'px';
    style.width = (this.settings[position] && this.settings[position].width) ? this.settings[position].width + 'px' : settings.border.width + 'px';

    this.main.appendChild(DIV);
  }

  this.top = this.main.childNodes[0];
  this.left = this.main.childNodes[1];
  this.bottom = this.main.childNodes[2];
  this.right = this.main.childNodes[3];

  this.topStyle = this.top.style;
  this.leftStyle = this.left.style;
  this.bottomStyle = this.bottom.style;
  this.rightStyle = this.right.style;

  this.cornerDefaultStyle = {
    width: '5px',
    height: '5px',
    borderWidth: '2px',
    borderStyle: 'solid',
    borderColor: '#FFF'
  };

  this.corner = this.main.childNodes[4];
  this.corner.className += ' corner';
  this.cornerStyle = this.corner.style;
  this.cornerStyle.width = this.cornerDefaultStyle.width;
  this.cornerStyle.height = this.cornerDefaultStyle.height;
  this.cornerStyle.border = [
    this.cornerDefaultStyle.borderWidth,
    this.cornerDefaultStyle.borderStyle,
    this.cornerDefaultStyle.borderColor
  ].join(' ');

  if(Handsontable.mobileBrowser) {
    createMultipleSelectorHandles.call(this);
  }

  this.disappear();
  if (!instance.wtTable.bordersHolder) {
    instance.wtTable.bordersHolder = document.createElement('div');
    instance.wtTable.bordersHolder.className = 'htBorders';
    instance.wtTable.hider.appendChild(instance.wtTable.bordersHolder);

  }
  instance.wtTable.bordersHolder.insertBefore(this.main, instance.wtTable.bordersHolder.firstChild);

  var down = false;

  eventManager.addEventListener(document.body, 'mousedown', function () {
    down = true;
  });

  eventManager.addEventListener(document.body, 'mouseup', function () {
    down = false;
  });

  /* jshint ignore:start */
  for (var c = 0, len = this.main.childNodes.length; c < len; c++) {

    eventManager.addEventListener(this.main.childNodes[c], 'mouseenter', function (event) {
      if (!down || !instance.getSetting('hideBorderOnMouseDownOver')) {
        return;
      }
      event.preventDefault();
      event.stopImmediatePropagation();

      var bounds = this.getBoundingClientRect();

      this.style.display = 'none';

      var isOutside = function (event) {
        if (event.clientY < Math.floor(bounds.top)) {
          return true;
        }
        if (event.clientY > Math.ceil(bounds.top + bounds.height)) {
          return true;
        }
        if (event.clientX < Math.floor(bounds.left)) {
          return true;
        }
        if (event.clientX > Math.ceil(bounds.left + bounds.width)) {
          return true;
        }
      };

      var handler = function (event) {
        if (isOutside(event)) {
          eventManager.removeEventListener(document.body, 'mousemove', handler);
          this.style.display = 'block';
        }
      };
      eventManager.addEventListener(document.body, 'mousemove', handler);
    });
  }
  /* jshint ignore:end */
}

/**
 * Show border around one or many cells
 * @param {Array} corners
 */
WalkontableBorder.prototype.appear = function (corners) {
  var isMultiple, fromTD, toTD, fromOffset, toOffset, containerOffset, top, minTop, left, minLeft, height, width;
  if (this.disabled) {
    return;
  }

  var instance = this.instance;

  var fromRow
    , fromColumn
    , toRow
    , toColumn
    , i
    , ilen
    , s;

  var isPartRange = function () {
    if(this.instance.selections.area.cellRange) {

      if (toRow != this.instance.selections.area.cellRange.to.row ||
          toColumn != this.instance.selections.area.cellRange.to.col) {
        return true;
      }
    }

    return false;
  };

  var updateMultipleSelectionHandlesPosition = function (top, left, width, height) {
    var handleWidth = parseInt(this.selectionHandles.styles.topLeft.width, 10)
      , hitAreaWidth = parseInt(this.selectionHandles.styles.topLeftHitArea.width, 10);

    this.selectionHandles.styles.topLeft.top = parseInt(top - handleWidth,10) + "px";
    this.selectionHandles.styles.topLeft.left = parseInt(left - handleWidth,10) + "px";

    this.selectionHandles.styles.topLeftHitArea.top = parseInt(top - (hitAreaWidth/4)*3,10) + "px";
    this.selectionHandles.styles.topLeftHitArea.left = parseInt(left - (hitAreaWidth/4)*3,10) + "px";

    this.selectionHandles.styles.bottomRight.top = parseInt(top + height,10) + "px";
    this.selectionHandles.styles.bottomRight.left = parseInt(left + width,10) + "px";

    this.selectionHandles.styles.bottomRightHitArea.top = parseInt(top + height - hitAreaWidth/4,10) + "px";
    this.selectionHandles.styles.bottomRightHitArea.left = parseInt(left + width - hitAreaWidth/4,10) + "px";

    if(this.settings.border.multipleSelectionHandlesVisible && this.settings.border.multipleSelectionHandlesVisible()) {
      this.selectionHandles.styles.topLeft.display = "block";
      this.selectionHandles.styles.topLeftHitArea.display = "block";
      if(!isPartRange.call(this)) {
        this.selectionHandles.styles.bottomRight.display = "block";
        this.selectionHandles.styles.bottomRightHitArea.display = "block";
      } else {
        this.selectionHandles.styles.bottomRight.display = "none";
        this.selectionHandles.styles.bottomRightHitArea.display = "none";
      }
    } else {
      this.selectionHandles.styles.topLeft.display = "none";
      this.selectionHandles.styles.bottomRight.display = "none";
      this.selectionHandles.styles.topLeftHitArea.display = "none";
      this.selectionHandles.styles.bottomRightHitArea.display = "none";
    }

    if(fromRow == this.instance.wtSettings.getSetting('fixedRowsTop') || fromColumn == this.instance.wtSettings.getSetting('fixedColumnsLeft')) {
      this.selectionHandles.styles.topLeft.zIndex = "9999";
      this.selectionHandles.styles.topLeftHitArea.zIndex = "9999";
    } else {
      this.selectionHandles.styles.topLeft.zIndex = "";
      this.selectionHandles.styles.topLeftHitArea.zIndex = "";
    }

  };

  if (instance.cloneOverlay instanceof WalkontableVerticalScrollbarNative || instance.cloneOverlay instanceof WalkontableCornerScrollbarNative) {
    ilen = instance.getSetting('fixedRowsTop');
  }
  else {
    ilen = instance.wtTable.getRenderedRowsCount();
  }

  for (i = 0; i < ilen; i++) {
    s = instance.wtTable.rowFilter.renderedToSource(i);
    if (s >= corners[0] && s <= corners[2]) {
      fromRow = s;
      break;
    }
  }

  for (i = ilen - 1; i >= 0; i--) {
    s = instance.wtTable.rowFilter.renderedToSource(i);
    if (s >= corners[0] && s <= corners[2]) {
      toRow = s;
      break;
    }
  }

  ilen = instance.wtTable.getRenderedColumnsCount();

  for (i = 0; i < ilen; i++) {
    s = instance.wtTable.columnFilter.renderedToSource(i);
    if (s >= corners[1] && s <= corners[3]) {
      fromColumn = s;
      break;
    }
  }

  for (i = ilen - 1; i >= 0; i--) {
    s = instance.wtTable.columnFilter.renderedToSource(i);
    if (s >= corners[1] && s <= corners[3]) {
      toColumn = s;
      break;
    }
  }

  if (fromRow !== void 0 && fromColumn !== void 0) {
    isMultiple = (fromRow !== toRow || fromColumn !== toColumn);
    fromTD = instance.wtTable.getCell(new WalkontableCellCoords(fromRow, fromColumn));
    toTD = isMultiple ? instance.wtTable.getCell(new WalkontableCellCoords(toRow, toColumn)) : fromTD;
    fromOffset = Handsontable.Dom.offset(fromTD);
    toOffset = isMultiple ? Handsontable.Dom.offset(toTD) : fromOffset;
    containerOffset = Handsontable.Dom.offset(instance.wtTable.TABLE);

    minTop = fromOffset.top;
    height = toOffset.top + Handsontable.Dom.outerHeight(toTD) - minTop;
    minLeft = fromOffset.left;
    width = toOffset.left + Handsontable.Dom.outerWidth(toTD) - minLeft;

    top = minTop - containerOffset.top - 1;
    left = minLeft - containerOffset.left - 1;

    var style = Handsontable.Dom.getComputedStyle(fromTD);
    if (parseInt(style['borderTopWidth'], 10) > 0) {
      top += 1;
      height = height > 0 ? height - 1 : 0;
    }
    if (parseInt(style['borderLeftWidth'], 10) > 0) {
      left += 1;
      width = width > 0 ? width - 1 : 0;
    }
  }
  else {
    this.disappear();
    return;
  }

  this.topStyle.top = top + 'px';
  this.topStyle.left = left + 'px';
  this.topStyle.width = width + 'px';
  this.topStyle.display = 'block';

  this.leftStyle.top = top + 'px';
  this.leftStyle.left = left + 'px';
  this.leftStyle.height = height + 'px';
  this.leftStyle.display = 'block';

  var delta = Math.floor(this.settings.border.width / 2);

  this.bottomStyle.top = top + height - delta + 'px';
  this.bottomStyle.left = left + 'px';
  this.bottomStyle.width = width + 'px';
  this.bottomStyle.display = 'block';

  this.rightStyle.top = top + 'px';
  this.rightStyle.left = left + width - delta + 'px';
  this.rightStyle.height = height + 1 + 'px';
  this.rightStyle.display = 'block';

  if (Handsontable.mobileBrowser || (!this.hasSetting(this.settings.border.cornerVisible) || isPartRange.call(this))) {
    this.cornerStyle.display = 'none';
  }
  else {
    this.cornerStyle.top = top + height - 4 + 'px';
    this.cornerStyle.left = left + width - 4 + 'px';
    this.cornerStyle.borderRightWidth = this.cornerDefaultStyle.borderWidth;
    this.cornerStyle.width = this.cornerDefaultStyle.width;
    this.cornerStyle.display = 'block';

    if (toColumn === this.instance.getSetting('totalColumns') - 1) {
      var scrollableElement = Handsontable.Dom.getScrollableElement(instance.wtTable.TABLE),
        cornerOverlappingContainer = toTD.offsetLeft + Handsontable.Dom.outerWidth(toTD) >= Handsontable.Dom.innerWidth(scrollableElement);

      if (cornerOverlappingContainer) {
        this.cornerStyle.left = Math.floor(left + width - 3 - parseInt(this.cornerDefaultStyle.width) / 2) + "px";
        this.cornerStyle.borderRightWidth = 0;
      }
    }
  }

  if (Handsontable.mobileBrowser) {
    updateMultipleSelectionHandlesPosition.call(this, top, left, width, height);
  }
};

/**
 * Hide border
 */
WalkontableBorder.prototype.disappear = function () {
  this.topStyle.display = 'none';
  this.leftStyle.display = 'none';
  this.bottomStyle.display = 'none';
  this.rightStyle.display = 'none';
  this.cornerStyle.display = 'none';

  if(Handsontable.mobileBrowser) {
    this.selectionHandles.styles.topLeft.display = 'none';
    this.selectionHandles.styles.bottomRight.display = 'none';
  }

};

WalkontableBorder.prototype.hasSetting = function (setting) {
  if (typeof setting === 'function') {
    return setting();
  }
  return !!setting;
};
<|endoftext|>


<commit_before>function WalkontableScroll(instance) {
  this.instance = instance;
}

/**
 * Scrolls viewport to a cell by minimum number of cells
 * @param {WalkontableCellCoords} coords
 */
WalkontableScroll.prototype.scrollViewport = function (coords) {
  if (!this.instance.drawn) {
    return;
  }

  var totalRows = this.instance.getSetting('totalRows')
    , totalColumns = this.instance.getSetting('totalColumns');

  if (coords.row < 0 || coords.row > totalRows - 1) {
    throw new Error('row ' + coords.row + ' does not exist');
  }

  if (coords.col < 0 || coords.col > totalColumns - 1) {
    throw new Error('column ' + coords.col + ' does not exist');
  }

    if (coords.row > this.instance.wtTable.getLastVisibleRow()) {
      this.instance.wtScrollbars.vertical.scrollTo(coords.row, true);
    } else if (coords.row >= this.instance.getSetting('fixedRowsTop') && coords.row < this.instance.wtTable.getFirstVisibleRow()){
      this.instance.wtScrollbars.vertical.scrollTo(coords.row);
    }

    if (coords.col > this.instance.wtTable.getLastVisibleColumn()) {
      this.instance.wtScrollbars.horizontal.scrollTo(coords.col, true);
    } else if (coords.col > this.instance.getSetting('fixedColumnsLeft') && coords.col < this.instance.wtTable.getFirstVisibleColumn()){
      this.instance.wtScrollbars.horizontal.scrollTo(coords.col);
    }

  //}
};
<commit_msg>Fix problem with keyboard navigation not scrolling to the far-left column #2093<commit_after>function WalkontableScroll(instance) {
  this.instance = instance;
}

/**
 * Scrolls viewport to a cell by minimum number of cells
 * @param {WalkontableCellCoords} coords
 */
WalkontableScroll.prototype.scrollViewport = function (coords) {
  if (!this.instance.drawn) {
    return;
  }

  var totalRows = this.instance.getSetting('totalRows')
    , totalColumns = this.instance.getSetting('totalColumns');

  if (coords.row < 0 || coords.row > totalRows - 1) {
    throw new Error('row ' + coords.row + ' does not exist');
  }

  if (coords.col < 0 || coords.col > totalColumns - 1) {
    throw new Error('column ' + coords.col + ' does not exist');
  }

    if (coords.row > this.instance.wtTable.getLastVisibleRow()) {
      this.instance.wtScrollbars.vertical.scrollTo(coords.row, true);
    } else if (coords.row >= this.instance.getSetting('fixedRowsTop') && coords.row < this.instance.wtTable.getFirstVisibleRow()){
      this.instance.wtScrollbars.vertical.scrollTo(coords.row);
    }

    if (coords.col > this.instance.wtTable.getLastVisibleColumn()) {
      this.instance.wtScrollbars.horizontal.scrollTo(coords.col, true);
    } else if (coords.col >= this.instance.getSetting('fixedColumnsLeft') && coords.col < this.instance.wtTable.getFirstVisibleColumn()){
      this.instance.wtScrollbars.horizontal.scrollTo(coords.col);
    }

  //}
};
<|endoftext|>


<commit_before>// jshint jasmine: true
// jshint maxlen: 130
/* globals waitsForPromise */

const utils = require('./spec-utils');

describe('Find declared taglibs', () => {
    // Dependencies
    let findDeclaredTaglibs;
    let sourceTlds;
    let registry;

    // State
    let pkg;

    // Utils functions
    let loadTestTld;

    beforeEach(() => {
        waitsForPromise(() => atom.packages.activatePackage('autocomplete-jsp'));

        runs(() => {
            pkg = atom.packages.getActivePackage('autocomplete-jsp');
            sourceTlds = require(`${pkg.path}/src/sources/tlds`);
            registry = require(`${pkg.path}/src/registry`);
            findDeclaredTaglibs = require(`${pkg.path}/src/find-declared-taglibs`).findDeclaredTaglibs;

            const functions = utils.mkUtilFunctions({
                pkg, sourceTlds, registry,
            });

            loadTestTld = functions.loadTestTld;
        });

        waitsForPromise(() => loadTestTld([
            'test.tld',
            'test2.tld',
            'test3.tld',
        ]));
    });

    it('finds taglibs declared by the taglib directive', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text =  '<%@ taglib\n' +
                'uri="http://example.com/jsp/test" prefix="prefixOfTag" %>\n';
            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'prefixOfTag');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('finds taglibs declared by the XML form of the taglib directive', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text = '<jsp:directive.taglib\n' +
                'uri="http://example.com/jsp/test" prefix="prefixOfTag" />\n';
            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'prefixOfTag');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('finds taglibs declared as xml namespace', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text = '<div xmlns:prefixOfTag="http://example.com/jsp/test"/>\n';
            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'prefixOfTag');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('ignores taglibs declarations after the cursor', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const textPre = '<jsp:directive.taglib\n' +
                'uri="http://example.com/jsp/test2" prefix="foo" />\n';

            const textAfter = '<jsp:directive.taglib\n' +
                'uri="http://example.com/jsp/test" prefix="bar" />\n';

            editor.setText(textPre + textAfter);

            waitsForPromise(() => findDeclaredTaglibs(textPre, editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'foo');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test2');

                const element2 = taglibs.find(item => item.prefix === 'bar');
                expect(element2).toBeUndefined();
            }));
        });
    });

    it('follows include directives and includes taglibs from included files', () => {
        waitsForPromise(() => atom.workspace.open('project-with-includes/main.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(3);

                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');

                const element2 = taglibs.find(item => item.prefix === 'test2');
                expect(element2).toBeDefined();
                expect(element2.desc.uri).toBe('http://example.com/jsp/test2');

                const element3 = taglibs.find(item => item.prefix === 'test3');
                expect(element3).toBeDefined();
                expect(element3.desc.uri).toBe('http://example.com/jsp/test3');
            }));
        });
    });

    it('follows include xml directives and includes taglibs from included files', () => {
        waitsForPromise(() => atom.workspace.open('project-with-includes-xml/main.jspx'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(3);

                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');

                const element2 = taglibs.find(item => item.prefix === 'test2');
                expect(element2).toBeDefined();
                expect(element2.desc.uri).toBe('http://example.com/jsp/test2');

                const element3 = taglibs.find(item => item.prefix === 'test3');
                expect(element3).toBeDefined();
                expect(element3.desc.uri).toBe('http://example.com/jsp/test3');
            }));
        });
    });

    it('stops when it detects a cyclic-include and does not crash', () => {
        waitsForPromise(() => atom.workspace.open('project-with-cyclic-includes/a.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(2);

                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');

                const element2 = taglibs.find(item => item.prefix === 'test2');
                expect(element2).toBeDefined();
                expect(element2.desc.uri).toBe('http://example.com/jsp/test2');
            }));
        });
    });

    it('ignores includes that do not exist and does not crash', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text = '<%@ include file="some/non/existingFile.jsp" %>\n' +
                '<jsp:directive.taglib uri="http://example.com/jsp/test" prefix="test"/>\n';

            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('immediately recognizes changes in the editor', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(0);
            }));

            runs(() => {
                const text = '<jsp:directive.taglib\n' +
                    'uri="http://example.com/jsp/test" prefix="foo" />\n';
                editor.setText(text);
            });

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                expect(taglibs.find(item => item.prefix === 'foo')).toBeDefined();
            }));
        });
    });

    it('resolves absolute paths relative to the webapp directory under src/main/webapp', () => {
        waitsForPromise(() => atom.workspace.open('project-with-absolute-includes/src/main/webapp/foo/bar.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('resolves absolute paths relative to the directory under src/main/* if no webapp directory exists', () => {
        waitsForPromise(() => atom.workspace.open('project-with-absolute-includes/src/main/resources/foo/bar.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('prefers src/main/* but also accepts src/*/*', () => {
        waitsForPromise(() => atom.workspace.open('project-with-absolute-includes/src/test/webapp/foo/bar.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    describe('fs actions', () => {
        const fs = require('fs');
        const path = require('path');

        let changedFile;
        let originalContent;

        afterEach(() => {
            if (changedFile) {
                fs.writeFileSync(changedFile, originalContent, 'utf-8');
                changedFile = null;
            }
        });

        it('recognizes changes in sub-includes after maximal 2000ms', () => {
            waitsForPromise(() => atom.workspace.open('project-with-includes/main.jsp'));

            runs(() => {
                const editor = atom.workspace.getActiveTextEditor();
                const openFile = editor.getPath();

                let resultHasChanged = false;
                function watchForChanges(previousLength) {
                    const start = Date.now();
                    resultHasChanged = false;

                    (function loop() {
                        findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                            if (start + 2000 > Date.now()) {
                                if (taglibs.length !== previousLength) {
                                    resultHasChanged = true;
                                } else {
                                    jasmine.Clock.real.setTimeout.call(this, () => {
                                        loop();
                                    }, 80);
                                }
                            }
                        });
                    })();
                }

                runs(() => {
                    changedFile = path.resolve(editor.getPath(), '..', 'tlds.jsp');
                    originalContent = fs.readFileSync(changedFile, 'utf-8');

                    waitsForPromise(() => findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                        expect(taglibs.length).toBe(3);
                        fs.writeFileSync(changedFile, '', 'utf-8');
                        watchForChanges(taglibs.length);
                    }));
                });

                waitsFor(() => resultHasChanged, 2000);

                waitsForPromise(() => findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                    expect(taglibs.length).toBe(1);
                    expect(taglibs.find(item => item.prefix === 'fooBarBazPrefix')).toBeUndefined();

                    const text = '<jsp:directive.taglib\n' +
                        'uri="http://example.com/jsp/test3" prefix="fooBarBazPrefix" />\n';
                        fs.writeFileSync(changedFile, text, 'utf-8');

                    watchForChanges(taglibs.length);
                }));

                waitsFor(() => resultHasChanged, 2000);

                waitsForPromise(() => findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                    expect(taglibs.length).toBe(2);
                    expect(taglibs.find(item => item.prefix === 'fooBarBazPrefix')).toBeDefined();
                }));
            });
        });
    });
});
<commit_msg>Add labels to waitsFor calls<commit_after>// jshint jasmine: true
// jshint maxlen: 130
/* globals waitsForPromise */

const utils = require('./spec-utils');

describe('Find declared taglibs', () => {
    // Dependencies
    let findDeclaredTaglibs;
    let sourceTlds;
    let registry;

    // State
    let pkg;

    // Utils functions
    let loadTestTld;

    beforeEach(() => {
        waitsForPromise(() => atom.packages.activatePackage('autocomplete-jsp'));

        runs(() => {
            pkg = atom.packages.getActivePackage('autocomplete-jsp');
            sourceTlds = require(`${pkg.path}/src/sources/tlds`);
            registry = require(`${pkg.path}/src/registry`);
            findDeclaredTaglibs = require(`${pkg.path}/src/find-declared-taglibs`).findDeclaredTaglibs;

            const functions = utils.mkUtilFunctions({
                pkg, sourceTlds, registry,
            });

            loadTestTld = functions.loadTestTld;
        });

        waitsForPromise(() => loadTestTld([
            'test.tld',
            'test2.tld',
            'test3.tld',
        ]));
    });

    it('finds taglibs declared by the taglib directive', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text =  '<%@ taglib\n' +
                'uri="http://example.com/jsp/test" prefix="prefixOfTag" %>\n';
            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'prefixOfTag');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('finds taglibs declared by the XML form of the taglib directive', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text = '<jsp:directive.taglib\n' +
                'uri="http://example.com/jsp/test" prefix="prefixOfTag" />\n';
            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'prefixOfTag');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('finds taglibs declared as xml namespace', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text = '<div xmlns:prefixOfTag="http://example.com/jsp/test"/>\n';
            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'prefixOfTag');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('ignores taglibs declarations after the cursor', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const textPre = '<jsp:directive.taglib\n' +
                'uri="http://example.com/jsp/test2" prefix="foo" />\n';

            const textAfter = '<jsp:directive.taglib\n' +
                'uri="http://example.com/jsp/test" prefix="bar" />\n';

            editor.setText(textPre + textAfter);

            waitsForPromise(() => findDeclaredTaglibs(textPre, editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);

                const element = taglibs.find(item => item.prefix === 'foo');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test2');

                const element2 = taglibs.find(item => item.prefix === 'bar');
                expect(element2).toBeUndefined();
            }));
        });
    });

    it('follows include directives and includes taglibs from included files', () => {
        waitsForPromise(() => atom.workspace.open('project-with-includes/main.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(3);

                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');

                const element2 = taglibs.find(item => item.prefix === 'test2');
                expect(element2).toBeDefined();
                expect(element2.desc.uri).toBe('http://example.com/jsp/test2');

                const element3 = taglibs.find(item => item.prefix === 'test3');
                expect(element3).toBeDefined();
                expect(element3.desc.uri).toBe('http://example.com/jsp/test3');
            }));
        });
    });

    it('follows include xml directives and includes taglibs from included files', () => {
        waitsForPromise(() => atom.workspace.open('project-with-includes-xml/main.jspx'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(3);

                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');

                const element2 = taglibs.find(item => item.prefix === 'test2');
                expect(element2).toBeDefined();
                expect(element2.desc.uri).toBe('http://example.com/jsp/test2');

                const element3 = taglibs.find(item => item.prefix === 'test3');
                expect(element3).toBeDefined();
                expect(element3.desc.uri).toBe('http://example.com/jsp/test3');
            }));
        });
    });

    it('stops when it detects a cyclic-include and does not crash', () => {
        waitsForPromise(() => atom.workspace.open('project-with-cyclic-includes/a.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(2);

                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');

                const element2 = taglibs.find(item => item.prefix === 'test2');
                expect(element2).toBeDefined();
                expect(element2.desc.uri).toBe('http://example.com/jsp/test2');
            }));
        });
    });

    it('ignores includes that do not exist and does not crash', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            const text = '<%@ include file="some/non/existingFile.jsp" %>\n' +
                '<jsp:directive.taglib uri="http://example.com/jsp/test" prefix="test"/>\n';

            editor.setText(text);

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('immediately recognizes changes in the editor', () => {
        waitsForPromise(() => atom.workspace.open('test.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(0);
            }));

            runs(() => {
                const text = '<jsp:directive.taglib\n' +
                    'uri="http://example.com/jsp/test" prefix="foo" />\n';
                editor.setText(text);
            });

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                expect(taglibs.find(item => item.prefix === 'foo')).toBeDefined();
            }));
        });
    });

    it('resolves absolute paths relative to the webapp directory under src/main/webapp', () => {
        waitsForPromise(() => atom.workspace.open('project-with-absolute-includes/src/main/webapp/foo/bar.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('resolves absolute paths relative to the directory under src/main/* if no webapp directory exists', () => {
        waitsForPromise(() => atom.workspace.open('project-with-absolute-includes/src/main/resources/foo/bar.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    it('prefers src/main/* but also accepts src/*/*', () => {
        waitsForPromise(() => atom.workspace.open('project-with-absolute-includes/src/test/webapp/foo/bar.jsp'));

        runs(() => {
            const editor = atom.workspace.getActiveTextEditor();

            waitsForPromise(() => findDeclaredTaglibs(editor.getText(), editor.getPath()).then(taglibs => {
                expect(taglibs.length).toBe(1);
                const element = taglibs.find(item => item.prefix === 'test');
                expect(element).toBeDefined();
                expect(element.desc.uri).toBe('http://example.com/jsp/test');
            }));
        });
    });

    describe('fs actions', () => {
        const fs = require('fs');
        const path = require('path');

        let changedFile;
        let originalContent;

        afterEach(() => {
            if (changedFile) {
                fs.writeFileSync(changedFile, originalContent, 'utf-8');
                changedFile = null;
            }
        });

        it('recognizes changes in sub-includes after maximal 2000ms', () => {
            waitsForPromise(() => atom.workspace.open('project-with-includes/main.jsp'));

            runs(() => {
                const editor = atom.workspace.getActiveTextEditor();
                const openFile = editor.getPath();

                let resultHasChanged = false;
                function watchForChanges(previousLength) {
                    const start = Date.now();
                    resultHasChanged = false;

                    (function loop() {
                        findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                            if (start + 2000 > Date.now()) {
                                if (taglibs.length !== previousLength) {
                                    resultHasChanged = true;
                                } else {
                                    jasmine.Clock.real.setTimeout.call(this, () => {
                                        loop();
                                    }, 80);
                                }
                            }
                        });
                    })();
                }

                runs(() => {
                    changedFile = path.resolve(editor.getPath(), '..', 'tlds.jsp');
                    originalContent = fs.readFileSync(changedFile, 'utf-8');

                    waitsForPromise(() => findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                        expect(taglibs.length).toBe(3);
                        fs.writeFileSync(changedFile, '', 'utf-8');
                        watchForChanges(taglibs.length);
                    }));
                });

                waitsFor('the taglibs to disappear', () => resultHasChanged, 2000);

                waitsForPromise(() => findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                    expect(taglibs.length).toBe(1);
                    expect(taglibs.find(item => item.prefix === 'fooBarBazPrefix')).toBeUndefined();

                    const text = '<jsp:directive.taglib\n' +
                        'uri="http://example.com/jsp/test3" prefix="fooBarBazPrefix" />\n';
                        fs.writeFileSync(changedFile, text, 'utf-8');

                    watchForChanges(taglibs.length);
                }));

                waitsFor('the new taglib to appear', () => resultHasChanged, 2000);

                waitsForPromise(() => findDeclaredTaglibs(editor.getText(), openFile).then(taglibs => {
                    expect(taglibs.length).toBe(2);
                    expect(taglibs.find(item => item.prefix === 'fooBarBazPrefix')).toBeDefined();
                }));
            });
        });
    });
});
<|endoftext|>


<commit_before>/*
 * Module dependencies.
 */

var phonegapbuild = require('../../lib/phonegap/util/phonegap-build'),
    PhoneGap = require('../../lib/phonegap'),
    events = require('events'),
    phonegap,
    emitter,
    appData,
    options,
    stdout;

/*
 * Specification: phonegap.remote.build(options, [callback])
 */

describe('phonegap.remote.build(options, [callback])', function() {
    beforeEach(function() {
        phonegap = new PhoneGap();
        options = {
            platforms: ['android']
        };
        appData = {
            id: '1234',
            title: 'My App',
            download: {
                android: '/api/v1/apps/322388/android'
            }
        };
        emitter = new events.EventEmitter();
        spyOn(process.stdout, 'write');
        spyOn(phonegap.remote, 'login');
        spyOn(phonegapbuild, 'build').andReturn(emitter);
    });

    it('should require options', function() {
        expect(function() {
            options = undefined;
            phonegap.remote.build(options, function(e) {});
        }).toThrow();
    });

    it('should require options.platforms', function() {
        expect(function() {
            options.platforms = undefined;
            phonegap.remote.build(options, function(e) {});
        }).toThrow();
    });

    it('should not require callback', function() {
        expect(function() {
            phonegap.remote.build(options);
        }).not.toThrow();
    });

    it('should return itself', function() {
        expect(phonegap.remote.build(options)).toEqual(phonegap);
    });

    it('should try to build the project', function() {
        phonegap.remote.build(options);
        expect(phonegapbuild.build).toHaveBeenCalledWith(
            {
                platforms: ['android']
            },
            jasmine.any(Function)
        );
    });

    describe('successful project build', function() {
        beforeEach(function() {
            phonegapbuild.build.andCallFake(function(opts, callback) {
                callback(null, appData);
                return emitter;
            });
        });

        it('should call callback without an error', function(done) {
            phonegap.remote.build(options, function(e, data) {
                expect(e).toBeNull();
                done();
            });
        });

        it('should call callback with a data object', function(done) {
            phonegap.remote.build(options, function(e, data) {
                expect(data).toEqual(appData);
                done();
            });
        });
    });

    describe('failed project build', function() {
        beforeEach(function() {
            phonegapbuild.build.andCallFake(function(opts, callback) {
                var e = new Error('could not connect to PhoneGap/Build');
                phonegapbuild.emit('error', e);
                callback(e);
                return emitter;
            });
        });

        it('should call callback with an error', function(done) {
            phonegap.remote.build(options, function(e, data) {
                expect(e).toEqual(jasmine.any(Error));
                done();
            });
        });

        it('should fire "error" event', function(done) {
            phonegap.on('error', function(e) {
                expect(e).toEqual(jasmine.any(Error));
                done();
            });
            phonegap.remote.build(options);
        });
    });
});
<commit_msg>[spec] Remove unused emitter logic from remote build.<commit_after>/*
 * Module dependencies.
 */

var phonegapbuild = require('../../lib/phonegap/util/phonegap-build'),
    PhoneGap = require('../../lib/phonegap'),
    events = require('events'),
    phonegap,
    appData,
    options,
    stdout;

/*
 * Specification: phonegap.remote.build(options, [callback])
 */

describe('phonegap.remote.build(options, [callback])', function() {
    beforeEach(function() {
        phonegap = new PhoneGap();
        options = {
            platforms: ['android']
        };
        appData = {
            id: '1234',
            title: 'My App',
            download: {
                android: '/api/v1/apps/322388/android'
            }
        };
        spyOn(process.stdout, 'write');
        spyOn(phonegap.remote, 'login');
        spyOn(phonegapbuild, 'build');
    });

    it('should require options', function() {
        expect(function() {
            options = undefined;
            phonegap.remote.build(options, function(e) {});
        }).toThrow();
    });

    it('should require options.platforms', function() {
        expect(function() {
            options.platforms = undefined;
            phonegap.remote.build(options, function(e) {});
        }).toThrow();
    });

    it('should not require callback', function() {
        expect(function() {
            phonegap.remote.build(options);
        }).not.toThrow();
    });

    it('should return itself', function() {
        expect(phonegap.remote.build(options)).toEqual(phonegap);
    });

    it('should try to build the project', function() {
        phonegap.remote.build(options);
        expect(phonegapbuild.build).toHaveBeenCalledWith(
            {
                platforms: ['android']
            },
            jasmine.any(Function)
        );
    });

    describe('successful project build', function() {
        beforeEach(function() {
            phonegapbuild.build.andCallFake(function(opts, callback) {
                callback(null, appData);
            });
        });

        it('should call callback without an error', function(done) {
            phonegap.remote.build(options, function(e, data) {
                expect(e).toBeNull();
                done();
            });
        });

        it('should call callback with a data object', function(done) {
            phonegap.remote.build(options, function(e, data) {
                expect(data).toEqual(appData);
                done();
            });
        });
    });

    describe('failed project build', function() {
        beforeEach(function() {
            phonegapbuild.build.andCallFake(function(opts, callback) {
                var e = new Error('could not connect to PhoneGap/Build');
                phonegapbuild.emit('error', e);
                callback(e);
            });
        });

        it('should call callback with an error', function(done) {
            phonegap.remote.build(options, function(e, data) {
                expect(e).toEqual(jasmine.any(Error));
                done();
            });
        });

        it('should fire "error" event', function(done) {
            phonegap.on('error', function(e) {
                expect(e).toEqual(jasmine.any(Error));
                done();
            });
            phonegap.remote.build(options);
        });
    });
});
<|endoftext|>


<commit_before>/**
* @description Utillity directives for key and field events
**/
angular.module('umbraco.directives')

.directive('onDragEnter', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragEnter);
            };
            elm.on("dragenter", f);
            scope.$on("$destroy", function(){ elm.off("dragenter", f);} );
        }
    };
})

.directive('onDragLeave', function () {
    return function (scope, elm, attrs) {
        var f = function (event) {
            var rect = this.getBoundingClientRect();
            var getXY = function getCursorPosition(event) {
                var x, y;

                if (typeof event.clientX === 'undefined') {
                    // try touch screen
                    x = event.pageX + document.documentElement.scrollLeft;
                    y = event.pageY + document.documentElement.scrollTop;
                } else {
                    x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                    y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
                }

                return { x: x, y : y };
            };

            var e = getXY(event.originalEvent);

            // Check the mouseEvent coordinates are outside of the rectangle
            if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
                scope.$apply(attrs.onDragLeave);
            }
        };

        elm.on("dragleave", f);
        scope.$on("$destroy", function(){ elm.off("dragleave", f);} );
    };
})

.directive('onDragOver', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragOver);
            };
            elm.on("dragover", f);
            scope.$on("$destroy", function(){ elm.off("dragover", f);} );
        }
    };
})

.directive('onDragStart', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragStart);
            };
            elm.on("dragstart", f);
            scope.$on("$destroy", function(){ elm.off("dragstart", f);} );
        }
    };
})

.directive('onDragEnd', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragEnd);
            };
            elm.on("dragend", f);
            scope.$on("$destroy", function(){ elm.off("dragend", f);} );
        }
    };
})

.directive('onDrop', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDrop);
            };
            elm.on("drop", f);
            scope.$on("$destroy", function(){ elm.off("drop", f);} );
        }
    };
})

.directive('onOutsideClick', function ($timeout, angularHelper) {
    return function (scope, element, attrs) {

        var eventBindings = [];

        function oneTimeClick(event) {
                var el = event.target.nodeName;

                //ignore link and button clicks
                var els = ["INPUT","A","BUTTON"];
                if(els.indexOf(el) >= 0){return;}

                // ignore clicks on new overlay
                var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour");
                if(parents.length > 0){
                    return;
                }

                // ignore clicks on dialog from old dialog service
                var oldDialog = $(event.target).parents("#old-dialog-service");
                if (oldDialog.length === 1) {
                    return;
                }

                // ignore clicks in tinyMCE dropdown(floatpanel)
                var floatpanel = $(event.target).closest(".mce-floatpanel");
                if (floatpanel.length === 1) {
                    return;
                }

                // ignore clicks in flatpickr datepicker
                var flatpickr = $(event.target).closest(".flatpickr-calendar");
                if (flatpickr.length === 1) {
                    return;
                }

                //ignore clicks inside this element
                if( $(element).has( $(event.target) ).length > 0 ){
                    return;
                }

                scope.$apply(attrs.onOutsideClick);
        }

        $timeout(function(){

            if ("bindClickOn" in attrs) {

                eventBindings.push(scope.$watch(function() {
                    return attrs.bindClickOn;
                }, function(newValue) {
                    if (newValue === "true") {
                        $(document).on("click", oneTimeClick);
                    } else {
                        $(document).off("click", oneTimeClick);
                    }
                }));

            } else {
                $(document).on("click", oneTimeClick);
            }

            scope.$on("$destroy", function() {
                $(document).off("click", oneTimeClick);

                // unbind watchers
                for (var e in eventBindings) {
                    eventBindings[e]();
                }

            });
        }); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution.

    };
})

.directive('onRightClick',function($parse){

    document.oncontextmenu = function (e) {
       if(e.target.hasAttribute('on-right-click')) {
           e.preventDefault();
           e.stopPropagation();
           return false;
       }
    };

    return function(scope,el,attrs){
        el.on('contextmenu',function(e){
            e.preventDefault();
            e.stopPropagation();
            var fn = $parse(attrs.onRightClick);
            scope.$apply(function () {
                fn(scope, { $event: event });
            });
            return false;
        });
    };
})

.directive('onDelayedMouseleave', function ($timeout, $parse) {
        return {

            restrict: 'A',

            link: function (scope, element, attrs, ctrl) {
                var active = false;
                var fn = $parse(attrs.onDelayedMouseleave);

                var leave_f = function(event) {
                    var callback = function() {
                        fn(scope, {$event:event});
                    };

                    active = false;
                    $timeout(function(){
                        if(active === false){
                            scope.$apply(callback);
                        }
                    }, 650);
                };

                var enter_f = function(event, args){
                    active = true;
                };

                element.on("mouseleave", leave_f);
                element.on("mouseenter", enter_f);

                //unsub events
                scope.$on("$destroy", function(){
                    element.off("mouseleave", leave_f);
                    element.off("mouseenter", enter_f);
                });
            }
        };
    });
<commit_msg>Make right-click menu work in Firefox<commit_after>/**
* @description Utillity directives for key and field events
**/
angular.module('umbraco.directives')

.directive('onDragEnter', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragEnter);
            };
            elm.on("dragenter", f);
            scope.$on("$destroy", function(){ elm.off("dragenter", f);} );
        }
    };
})

.directive('onDragLeave', function () {
    return function (scope, elm, attrs) {
        var f = function (event) {
            var rect = this.getBoundingClientRect();
            var getXY = function getCursorPosition(event) {
                var x, y;

                if (typeof event.clientX === 'undefined') {
                    // try touch screen
                    x = event.pageX + document.documentElement.scrollLeft;
                    y = event.pageY + document.documentElement.scrollTop;
                } else {
                    x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                    y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
                }

                return { x: x, y : y };
            };

            var e = getXY(event.originalEvent);

            // Check the mouseEvent coordinates are outside of the rectangle
            if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
                scope.$apply(attrs.onDragLeave);
            }
        };

        elm.on("dragleave", f);
        scope.$on("$destroy", function(){ elm.off("dragleave", f);} );
    };
})

.directive('onDragOver', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragOver);
            };
            elm.on("dragover", f);
            scope.$on("$destroy", function(){ elm.off("dragover", f);} );
        }
    };
})

.directive('onDragStart', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragStart);
            };
            elm.on("dragstart", f);
            scope.$on("$destroy", function(){ elm.off("dragstart", f);} );
        }
    };
})

.directive('onDragEnd', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDragEnd);
            };
            elm.on("dragend", f);
            scope.$on("$destroy", function(){ elm.off("dragend", f);} );
        }
    };
})

.directive('onDrop', function () {
    return {
        link: function (scope, elm, attrs) {
            var f = function () {
                scope.$apply(attrs.onDrop);
            };
            elm.on("drop", f);
            scope.$on("$destroy", function(){ elm.off("drop", f);} );
        }
    };
})

.directive('onOutsideClick', function ($timeout, angularHelper) {
    return function (scope, element, attrs) {

        var eventBindings = [];

        function oneTimeClick(event) {
                var el = event.target.nodeName;

                //ignore link and button clicks
                var els = ["INPUT","A","BUTTON"];
                if(els.indexOf(el) >= 0){return;}

                // ignore clicks on new overlay
                var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour");
                if(parents.length > 0){
                    return;
                }

                // ignore clicks on dialog from old dialog service
                var oldDialog = $(event.target).parents("#old-dialog-service");
                if (oldDialog.length === 1) {
                    return;
                }

                // ignore clicks in tinyMCE dropdown(floatpanel)
                var floatpanel = $(event.target).closest(".mce-floatpanel");
                if (floatpanel.length === 1) {
                    return;
                }

                // ignore clicks in flatpickr datepicker
                var flatpickr = $(event.target).closest(".flatpickr-calendar");
                if (flatpickr.length === 1) {
                    return;
                }

                //ignore clicks inside this element
                if( $(element).has( $(event.target) ).length > 0 ){
                    return;
                }

                scope.$apply(attrs.onOutsideClick);
        }

        $timeout(function(){

            if ("bindClickOn" in attrs) {

                eventBindings.push(scope.$watch(function() {
                    return attrs.bindClickOn;
                }, function(newValue) {
                    if (newValue === "true") {
                        $(document).on("click", oneTimeClick);
                    } else {
                        $(document).off("click", oneTimeClick);
                    }
                }));

            } else {
                $(document).on("click", oneTimeClick);
            }

            scope.$on("$destroy", function() {
                $(document).off("click", oneTimeClick);

                // unbind watchers
                for (var e in eventBindings) {
                    eventBindings[e]();
                }

            });
        }); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution.

    };
})

.directive('onRightClick',function($parse){

    document.oncontextmenu = function (e) {
       if(e.target.hasAttribute('on-right-click')) {
           e.preventDefault();
           e.stopPropagation();
           return false;
       }
    };

    return function(scope,el,attrs){
        el.on('contextmenu',function(e){
            e.preventDefault();
            e.stopPropagation();
            var fn = $parse(attrs.onRightClick);
            scope.$apply(function () {
                fn(scope, { $event: e });
            });
            return false;
        });
    };
})

.directive('onDelayedMouseleave', function ($timeout, $parse) {
        return {

            restrict: 'A',

            link: function (scope, element, attrs, ctrl) {
                var active = false;
                var fn = $parse(attrs.onDelayedMouseleave);

                var leave_f = function(event) {
                    var callback = function() {
                        fn(scope, {$event:event});
                    };

                    active = false;
                    $timeout(function(){
                        if(active === false){
                            scope.$apply(callback);
                        }
                    }, 650);
                };

                var enter_f = function(event, args){
                    active = true;
                };

                element.on("mouseleave", leave_f);
                element.on("mouseenter", enter_f);

                //unsub events
                scope.$on("$destroy", function(){
                    element.off("mouseleave", leave_f);
                    element.off("mouseenter", enter_f);
                });
            }
        };
    });
<|endoftext|>