<commit_before>/**
 * Handsontable TableView constructor
 * @param {Object} instance
 */
Handsontable.TableView = function (instance) {
  var that = this

  this.eventManager = Handsontable.eventManager(instance);
  this.instance = instance;
  this.settings = instance.getSettings();

  var originalStyle = instance.rootElement.getAttribute('style');
  if(originalStyle) {
    instance.rootElement.setAttribute('data-originalstyle', originalStyle); //needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
  }

  Handsontable.Dom.addClass(instance.rootElement,'handsontable');
//  instance.rootElement.addClass('handsontable');

  var table = document.createElement('TABLE');
  table.className = 'htCore';
  this.THEAD = document.createElement('THEAD');
  table.appendChild(this.THEAD);
  this.TBODY = document.createElement('TBODY');
  table.appendChild(this.TBODY);

  instance.table = table;

  instance.container.insertBefore(table, instance.container.firstChild);

  this.eventManager.addEventListener(instance.rootElement,'mousedown', function (event) {
    if (!that.isTextSelectionAllowed(event.target)) {
      clearTextSelection();
      event.preventDefault();
      window.focus(); //make sure that window that contains HOT is active. Important when HOT is in iframe.
    }
  });

  this.eventManager.addEventListener(document.documentElement, 'keyup',function (event) {
    debugger
    if (instance.selection.isInProgress() && !event.shiftKey) {
      instance.selection.finish();
    }
  });

  var isMouseDown;
  this.isMouseDown = function () {
    return isMouseDown;
  };

  this.eventManager.addEventListener(document.documentElement, 'mouseup', function (event) {
    if (instance.selection.isInProgress() && event.which === 1) { //is left mouse button
      instance.selection.finish();
    }

    isMouseDown = false;

    if (Handsontable.helper.isOutsideInput(document.activeElement)) {
      instance.unlisten();
    }
  });

  this.eventManager.addEventListener(document.documentElement, 'mousedown',function (event) {
    var next = event.target;

    if (isMouseDown) {
      return; //it must have been started in a cell
    }

    if (next !== that.wt.wtTable.spreader) { //immediate click on "spreader" means click on the right side of vertical scrollbar
      while (next !== document.documentElement) {
        if (next === null) {
          return; //click on something that was a row but now is detached (possibly because your click triggered a rerender)
        }
       if (next === instance.rootElement) {
          return; //click inside container
        }
        next = next.parentNode;
      }
    }

    //function did not return until here, we have an outside click!

    if (that.settings.outsideClickDeselects) {
      instance.deselectCell();
    }
    else {
      instance.destroyEditor();
    }
  });

  this.eventManager.addEventListener(table, 'selectstart', function (event) {
    if (that.settings.fragmentSelection) {
      return;
    }

    //https://github.com/handsontable/jquery-handsontable/issues/160
    //selectstart is IE only event. Prevent text from being selected when performing drag down in IE8
    event.preventDefault();
  });

  var clearTextSelection = function () {
    //http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
    if (window.getSelection) {
      if (window.getSelection().empty) {  // Chrome
        window.getSelection().empty();
      } else if (window.getSelection().removeAllRanges) {  // Firefox
        window.getSelection().removeAllRanges();
      }
    } else if (document.selection) {  // IE?
      document.selection.empty();
    }
  };

  var selections = [
    new WalkontableSelection({
      className: 'current',
      border: {
        width: 2,
        color: '#5292F7',
        //style: 'solid', //not used
        cornerVisible: function () {
          return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
        }
      }
    }),
    new WalkontableSelection({
      className: 'area',
      border: {
        width: 1,
        color: '#89AFF9',
        //style: 'solid', // not used
        cornerVisible: function () {
          return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
        }
      }
    }),
    new WalkontableSelection({
      className: 'highlight',
      highlightRowClassName: that.settings.currentRowClassName,
      highlightColumnClassName: that.settings.currentColClassName
    }),
    new WalkontableSelection({
      className: 'fill',
      border: {
        width: 1,
        color: 'red'
        //style: 'solid' // not used
      }
    })
  ];
  selections.current = selections[0];
  selections.area = selections[1];
  selections.highlight = selections[2];
  selections.fill = selections[3];

  var walkontableConfig = {
    debug: function () {
      return that.settings.debug;
    },
    table: table,
    stretchH: this.settings.stretchH,
    data: instance.getDataAtCell,
    totalRows: instance.countRows,
    totalColumns: instance.countCols,
    fixedColumnsLeft: function () {
      return that.settings.fixedColumnsLeft;
    },
    fixedRowsTop: function () {
      return that.settings.fixedRowsTop;
    },
    renderAllRows: that.settings.renderAllRows,
    rowHeaders: function () {
      var arr = [];
      if(instance.hasRowHeaders()) {
        arr.push(function (index, TH) {
          that.appendRowHeader(index, TH);
        });
      }
      Handsontable.hooks.run(instance, 'afterGetRowHeaderRenderers', arr);
      return arr;
    },
    columnHeaders: function () {

      var arr = [];
      if(instance.hasColHeaders()) {
        arr.push(function (index, TH) {
          that.appendColHeader(index, TH);
        });
      }
      Handsontable.hooks.run(instance, 'afterGetColumnHeaderRenderers', arr);
      return arr;
    },
    columnWidth: instance.getColWidth,
    rowHeight: instance.getRowHeight,
    cellRenderer: function (row, col, TD) {

      var prop = that.instance.colToProp(col)
        , cellProperties = that.instance.getCellMeta(row, col)
        , renderer = that.instance.getCellRenderer(cellProperties);

      var value = that.instance.getDataAtRowProp(row, prop);

      renderer(that.instance, TD, row, col, prop, value, cellProperties);
      Handsontable.hooks.run(that.instance, 'afterRenderer', TD, row, col, prop, value, cellProperties);

    },
    selections: selections,
    hideBorderOnMouseDownOver: function () {
      return that.settings.fragmentSelection;
    },
    onCellMouseDown: function (event, coords, TD, wt) {
      instance.listen();
      that.activeWt = wt;

      isMouseDown = true;

      Handsontable.hooks.run(instance, 'beforeOnCellMouseDown', event, coords, TD);

      if (event != null && event.isImmediatePropagationEnabled == null) {
        event.stopImmediatePropagation = function () {
          this.isImmediatePropagationEnabled = false;
          this.cancelBubble = true;
        };
        event.isImmediatePropagationEnabled = true;
        event.isImmediatePropagationStopped = function () {
          return !this.isImmediatePropagationEnabled;
        };
      }

      if (!event.isImmediatePropagationStopped()) {

        if (event.button === 2 && instance.selection.inInSelection(coords)) { //right mouse button
          //do nothing
        }
        else if (event.shiftKey) {
          if (coords.row >= 0 && coords.col >= 0) {
            instance.selection.setRangeEnd(coords);
          }
        }
        else {
          if (coords.row < 0 || coords.col < 0) {
            if (coords.row < 0) {
              instance.selectCell(0, coords.col, instance.countRows() - 1, coords.col);
              instance.selection.setSelectedHeaders(false, true);
            }
            if (coords.col < 0) {
              instance.selectCell(coords.row, 0, coords.row, instance.countCols() - 1);
              instance.selection.setSelectedHeaders(true, false);
            }
          }
          else {
            instance.selection.setRangeStart(coords);
          }
        }

        Handsontable.hooks.run(instance, 'afterOnCellMouseDown', event, coords, TD);

        that.activeWt = that.wt;
      }
    },
    /*onCellMouseOut: function (/*event, coords, TD* /) {
     if (isMouseDown && that.settings.fragmentSelection === 'single') {
     clearTextSelection(); //otherwise text selection blinks during multiple cells selection
     }
     },*/
    onCellMouseOver: function (event, coords, TD, wt) {
      that.activeWt = wt;
      if (coords.row >= 0 && coords.col >= 0) { //is not a header
        if (isMouseDown) {
          /*if (that.settings.fragmentSelection === 'single') {
           clearTextSelection(); //otherwise text selection blinks during multiple cells selection
           }*/
          instance.selection.setRangeEnd(coords);
        }
      } else {
        if (isMouseDown) {
          // multi select columns
          if (coords.row < 0) {
            instance.selection.setRangeEnd(new WalkontableCellCoords(instance.countRows() - 1, coords.col));
            instance.selection.setSelectedHeaders(false, true);
          }

          // multi select rows
          if (coords.col < 0) {
            instance.selection.setRangeEnd(new WalkontableCellCoords(coords.row, instance.countCols() - 1));
            instance.selection.setSelectedHeaders(true, false);
          }
        }
      }

      Handsontable.hooks.run(instance, 'afterOnCellMouseOver', event, coords, TD);
      that.activeWt = that.wt;
    },
    onCellCornerMouseDown: function (event) {
      event.preventDefault();
      Handsontable.hooks.run(instance, 'afterOnCellCornerMouseDown', event);
    },
    beforeDraw: function (force) {
      that.beforeRender(force);
    },
    onDraw: function (force) {
      that.onDraw(force);
    },
    onScrollVertically: function () {
      instance.runHooks('afterScrollVertically');
    },
    onScrollHorizontally: function () {
      instance.runHooks('afterScrollHorizontally');
    },
    onBeforeDrawBorders: function (corners, borderClassName) {
      instance.runHooks('beforeDrawBorders', corners, borderClassName);
    },
    viewportRowCalculatorOverride: function (calc) {
      if (that.settings.viewportRowRenderingOffset) {
        calc.renderStartRow = Math.max(calc.renderStartRow - that.settings.viewportRowRenderingOffset, 0);
        calc.renderEndRow = Math.min(calc.renderEndRow + that.settings.viewportRowRenderingOffset, instance.countRows() - 1);
      }
      instance.runHooks('afterViewportRowCalculatorOverride', calc);
    }
  };

  Handsontable.hooks.run(instance, 'beforeInitWalkontable', walkontableConfig);

  this.wt = new Walkontable(walkontableConfig);
  this.activeWt = this.wt;

  this.eventManager.addEventListener(that.wt.wtTable.spreader, 'mousedown', function (event) {
    if (event.target === that.wt.wtTable.spreader && event.which === 3) { //right mouse button exactly on spreader means right clickon the right hand side of vertical scrollbar
      Handsontable.helper.stopPropagation(event);
      //event.stopPropagation();
    }
  });

  this.eventManager.addEventListener(that.wt.wtTable.spreader, 'contextmenu', function (event) {
    if (event.target === that.wt.wtTable.spreader && event.which === 3) { //right mouse button exactly on spreader means right clickon the right hand side of vertical scrollbar
      Handsontable.helper.stopPropagation(event);
      //event.stopPropagation();
    }
  });

  this.eventManager.addEventListener(document.documentElement, 'click', function () {
    if (that.settings.observeDOMVisibility) {
      if (that.wt.drawInterrupted) {
        that.instance.forceFullRender = true;
        that.render();
      }
    }
  });
};

Handsontable.TableView.prototype.isTextSelectionAllowed = function (el) {
  if (Handsontable.helper.isInput(el)) {
    return (true);
  }
  if (this.settings.fragmentSelection && Handsontable.Dom.isChildOf(el, this.TBODY)) {
    return (true);
  }
  return false;
};

Handsontable.TableView.prototype.isCellEdited = function () {
  var activeEditor = this.instance.getActiveEditor();
  return activeEditor && activeEditor.isOpened();
};

Handsontable.TableView.prototype.beforeRender = function (force) {
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'beforeRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.onDraw = function (force) {
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'afterRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.render = function () {
  this.wt.draw(!this.instance.forceFullRender);
  this.instance.forceFullRender = false;
//  this.instance.rootElement.triggerHandler('render.handsontable');
};

/**
 * Returns td object given coordinates
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.getCellAtCoords = function (coords) {
  var td = this.wt.wtTable.getCell(coords);
  if (td < 0) { //there was an exit code (cell is out of bounds)
    return null;
  }
  else {
    return td;
  }
};

/**
 * Scroll viewport to selection
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.scrollViewport = function (coords) {
  this.wt.scrollViewport(coords);
};

/**
 * Append row header to a TH element
 * @param row
 * @param TH
 */
Handsontable.TableView.prototype.appendRowHeader = function (row, TH) {
  var DIV = document.createElement('DIV'),
    SPAN = document.createElement('SPAN');

  DIV.className = 'relative';
  SPAN.className = 'rowHeader';

  if (row > -1) {
    Handsontable.Dom.fastInnerHTML(SPAN, this.instance.getRowHeader(row));
  } else {
    Handsontable.Dom.fastInnerText(SPAN, '\u00A0');
  }

  DIV.appendChild(SPAN);
  Handsontable.Dom.empty(TH);

  TH.appendChild(DIV);

  Handsontable.hooks.run(this.instance, 'afterGetRowHeader', row, TH);
};

/**
 * Append column header to a TH element
 * @param col
 * @param TH
 */
Handsontable.TableView.prototype.appendColHeader = function (col, TH) {
  var DIV = document.createElement('DIV')
    , SPAN = document.createElement('SPAN');

  DIV.className = 'relative';
  SPAN.className = 'colHeader';

  if (col > -1) {
    Handsontable.Dom.fastInnerHTML(SPAN, this.instance.getColHeader(col));
  } else {
    Handsontable.Dom.fastInnerText(SPAN, '\u00A0');
  }
  DIV.appendChild(SPAN);

  Handsontable.Dom.empty(TH);
  TH.appendChild(DIV);
  Handsontable.hooks.run(this.instance, 'afterGetColHeader', col, TH);
};

/**
 * Given a element's left position relative to the viewport, returns maximum element width until the right edge of the viewport (before scrollbar)
 * @param {Number} leftOffset
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementWidth = function (leftOffset) {
  var workspaceWidth = this.wt.wtViewport.getWorkspaceWidth();
  var maxWidth = workspaceWidth - leftOffset;
  return maxWidth > 0 ? maxWidth : 0;
};

/**
 * Given a element's top position relative to the viewport, returns maximum element height until the bottom edge of the viewport (before scrollbar)
 * @param {Number} topOffset
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementHeight = function (topOffset) {
  var workspaceHeight = this.wt.wtViewport.getWorkspaceHeight();
  var maxHeight = workspaceHeight - topOffset;
  return maxHeight > 0 ? maxHeight : 0;
};

Handsontable.TableView.prototype.mainViewIsActive = function () {
  return this.wt === this.activeWt;
};

Handsontable.TableView.prototype.destroy = function () {
  this.wt.destroy();
  this.eventManager.clear();
};
<commit_msg>removed debugger<commit_after>/**
 * Handsontable TableView constructor
 * @param {Object} instance
 */
Handsontable.TableView = function (instance) {
  var that = this

  this.eventManager = Handsontable.eventManager(instance);
  this.instance = instance;
  this.settings = instance.getSettings();

  var originalStyle = instance.rootElement.getAttribute('style');
  if(originalStyle) {
    instance.rootElement.setAttribute('data-originalstyle', originalStyle); //needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
  }

  Handsontable.Dom.addClass(instance.rootElement,'handsontable');
//  instance.rootElement.addClass('handsontable');

  var table = document.createElement('TABLE');
  table.className = 'htCore';
  this.THEAD = document.createElement('THEAD');
  table.appendChild(this.THEAD);
  this.TBODY = document.createElement('TBODY');
  table.appendChild(this.TBODY);

  instance.table = table;

  instance.container.insertBefore(table, instance.container.firstChild);

  this.eventManager.addEventListener(instance.rootElement,'mousedown', function (event) {
    if (!that.isTextSelectionAllowed(event.target)) {
      clearTextSelection();
      event.preventDefault();
      window.focus(); //make sure that window that contains HOT is active. Important when HOT is in iframe.
    }
  });

  this.eventManager.addEventListener(document.documentElement, 'keyup',function (event) {
    if (instance.selection.isInProgress() && !event.shiftKey) {
      instance.selection.finish();
    }
  });

  var isMouseDown;
  this.isMouseDown = function () {
    return isMouseDown;
  };

  this.eventManager.addEventListener(document.documentElement, 'mouseup', function (event) {
    if (instance.selection.isInProgress() && event.which === 1) { //is left mouse button
      instance.selection.finish();
    }

    isMouseDown = false;

    if (Handsontable.helper.isOutsideInput(document.activeElement)) {
      instance.unlisten();
    }
  });

  this.eventManager.addEventListener(document.documentElement, 'mousedown',function (event) {
    var next = event.target;

    if (isMouseDown) {
      return; //it must have been started in a cell
    }

    if (next !== that.wt.wtTable.spreader) { //immediate click on "spreader" means click on the right side of vertical scrollbar
      while (next !== document.documentElement) {
        if (next === null) {
          return; //click on something that was a row but now is detached (possibly because your click triggered a rerender)
        }
       if (next === instance.rootElement) {
          return; //click inside container
        }
        next = next.parentNode;
      }
    }

    //function did not return until here, we have an outside click!

    if (that.settings.outsideClickDeselects) {
      instance.deselectCell();
    }
    else {
      instance.destroyEditor();
    }
  });

  this.eventManager.addEventListener(table, 'selectstart', function (event) {
    if (that.settings.fragmentSelection) {
      return;
    }

    //https://github.com/handsontable/jquery-handsontable/issues/160
    //selectstart is IE only event. Prevent text from being selected when performing drag down in IE8
    event.preventDefault();
  });

  var clearTextSelection = function () {
    //http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
    if (window.getSelection) {
      if (window.getSelection().empty) {  // Chrome
        window.getSelection().empty();
      } else if (window.getSelection().removeAllRanges) {  // Firefox
        window.getSelection().removeAllRanges();
      }
    } else if (document.selection) {  // IE?
      document.selection.empty();
    }
  };

  var selections = [
    new WalkontableSelection({
      className: 'current',
      border: {
        width: 2,
        color: '#5292F7',
        //style: 'solid', //not used
        cornerVisible: function () {
          return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
        }
      }
    }),
    new WalkontableSelection({
      className: 'area',
      border: {
        width: 1,
        color: '#89AFF9',
        //style: 'solid', // not used
        cornerVisible: function () {
          return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
        }
      }
    }),
    new WalkontableSelection({
      className: 'highlight',
      highlightRowClassName: that.settings.currentRowClassName,
      highlightColumnClassName: that.settings.currentColClassName
    }),
    new WalkontableSelection({
      className: 'fill',
      border: {
        width: 1,
        color: 'red'
        //style: 'solid' // not used
      }
    })
  ];
  selections.current = selections[0];
  selections.area = selections[1];
  selections.highlight = selections[2];
  selections.fill = selections[3];

  var walkontableConfig = {
    debug: function () {
      return that.settings.debug;
    },
    table: table,
    stretchH: this.settings.stretchH,
    data: instance.getDataAtCell,
    totalRows: instance.countRows,
    totalColumns: instance.countCols,
    fixedColumnsLeft: function () {
      return that.settings.fixedColumnsLeft;
    },
    fixedRowsTop: function () {
      return that.settings.fixedRowsTop;
    },
    renderAllRows: that.settings.renderAllRows,
    rowHeaders: function () {
      var arr = [];
      if(instance.hasRowHeaders()) {
        arr.push(function (index, TH) {
          that.appendRowHeader(index, TH);
        });
      }
      Handsontable.hooks.run(instance, 'afterGetRowHeaderRenderers', arr);
      return arr;
    },
    columnHeaders: function () {

      var arr = [];
      if(instance.hasColHeaders()) {
        arr.push(function (index, TH) {
          that.appendColHeader(index, TH);
        });
      }
      Handsontable.hooks.run(instance, 'afterGetColumnHeaderRenderers', arr);
      return arr;
    },
    columnWidth: instance.getColWidth,
    rowHeight: instance.getRowHeight,
    cellRenderer: function (row, col, TD) {

      var prop = that.instance.colToProp(col)
        , cellProperties = that.instance.getCellMeta(row, col)
        , renderer = that.instance.getCellRenderer(cellProperties);

      var value = that.instance.getDataAtRowProp(row, prop);

      renderer(that.instance, TD, row, col, prop, value, cellProperties);
      Handsontable.hooks.run(that.instance, 'afterRenderer', TD, row, col, prop, value, cellProperties);

    },
    selections: selections,
    hideBorderOnMouseDownOver: function () {
      return that.settings.fragmentSelection;
    },
    onCellMouseDown: function (event, coords, TD, wt) {
      instance.listen();
      that.activeWt = wt;

      isMouseDown = true;

      Handsontable.hooks.run(instance, 'beforeOnCellMouseDown', event, coords, TD);

      if (event != null && event.isImmediatePropagationEnabled == null) {
        event.stopImmediatePropagation = function () {
          this.isImmediatePropagationEnabled = false;
          this.cancelBubble = true;
        };
        event.isImmediatePropagationEnabled = true;
        event.isImmediatePropagationStopped = function () {
          return !this.isImmediatePropagationEnabled;
        };
      }

      if (!event.isImmediatePropagationStopped()) {

        if (event.button === 2 && instance.selection.inInSelection(coords)) { //right mouse button
          //do nothing
        }
        else if (event.shiftKey) {
          if (coords.row >= 0 && coords.col >= 0) {
            instance.selection.setRangeEnd(coords);
          }
        }
        else {
          if (coords.row < 0 || coords.col < 0) {
            if (coords.row < 0) {
              instance.selectCell(0, coords.col, instance.countRows() - 1, coords.col);
              instance.selection.setSelectedHeaders(false, true);
            }
            if (coords.col < 0) {
              instance.selectCell(coords.row, 0, coords.row, instance.countCols() - 1);
              instance.selection.setSelectedHeaders(true, false);
            }
          }
          else {
            instance.selection.setRangeStart(coords);
          }
        }

        Handsontable.hooks.run(instance, 'afterOnCellMouseDown', event, coords, TD);

        that.activeWt = that.wt;
      }
    },
    /*onCellMouseOut: function (/*event, coords, TD* /) {
     if (isMouseDown && that.settings.fragmentSelection === 'single') {
     clearTextSelection(); //otherwise text selection blinks during multiple cells selection
     }
     },*/
    onCellMouseOver: function (event, coords, TD, wt) {
      that.activeWt = wt;
      if (coords.row >= 0 && coords.col >= 0) { //is not a header
        if (isMouseDown) {
          /*if (that.settings.fragmentSelection === 'single') {
           clearTextSelection(); //otherwise text selection blinks during multiple cells selection
           }*/
          instance.selection.setRangeEnd(coords);
        }
      } else {
        if (isMouseDown) {
          // multi select columns
          if (coords.row < 0) {
            instance.selection.setRangeEnd(new WalkontableCellCoords(instance.countRows() - 1, coords.col));
            instance.selection.setSelectedHeaders(false, true);
          }

          // multi select rows
          if (coords.col < 0) {
            instance.selection.setRangeEnd(new WalkontableCellCoords(coords.row, instance.countCols() - 1));
            instance.selection.setSelectedHeaders(true, false);
          }
        }
      }

      Handsontable.hooks.run(instance, 'afterOnCellMouseOver', event, coords, TD);
      that.activeWt = that.wt;
    },
    onCellCornerMouseDown: function (event) {
      event.preventDefault();
      Handsontable.hooks.run(instance, 'afterOnCellCornerMouseDown', event);
    },
    beforeDraw: function (force) {
      that.beforeRender(force);
    },
    onDraw: function (force) {
      that.onDraw(force);
    },
    onScrollVertically: function () {
      instance.runHooks('afterScrollVertically');
    },
    onScrollHorizontally: function () {
      instance.runHooks('afterScrollHorizontally');
    },
    onBeforeDrawBorders: function (corners, borderClassName) {
      instance.runHooks('beforeDrawBorders', corners, borderClassName);
    },
    viewportRowCalculatorOverride: function (calc) {
      if (that.settings.viewportRowRenderingOffset) {
        calc.renderStartRow = Math.max(calc.renderStartRow - that.settings.viewportRowRenderingOffset, 0);
        calc.renderEndRow = Math.min(calc.renderEndRow + that.settings.viewportRowRenderingOffset, instance.countRows() - 1);
      }
      instance.runHooks('afterViewportRowCalculatorOverride', calc);
    }
  };

  Handsontable.hooks.run(instance, 'beforeInitWalkontable', walkontableConfig);

  this.wt = new Walkontable(walkontableConfig);
  this.activeWt = this.wt;

  this.eventManager.addEventListener(that.wt.wtTable.spreader, 'mousedown', function (event) {
    if (event.target === that.wt.wtTable.spreader && event.which === 3) { //right mouse button exactly on spreader means right clickon the right hand side of vertical scrollbar
      Handsontable.helper.stopPropagation(event);
      //event.stopPropagation();
    }
  });

  this.eventManager.addEventListener(that.wt.wtTable.spreader, 'contextmenu', function (event) {
    if (event.target === that.wt.wtTable.spreader && event.which === 3) { //right mouse button exactly on spreader means right clickon the right hand side of vertical scrollbar
      Handsontable.helper.stopPropagation(event);
      //event.stopPropagation();
    }
  });

  this.eventManager.addEventListener(document.documentElement, 'click', function () {
    if (that.settings.observeDOMVisibility) {
      if (that.wt.drawInterrupted) {
        that.instance.forceFullRender = true;
        that.render();
      }
    }
  });
};

Handsontable.TableView.prototype.isTextSelectionAllowed = function (el) {
  if (Handsontable.helper.isInput(el)) {
    return (true);
  }
  if (this.settings.fragmentSelection && Handsontable.Dom.isChildOf(el, this.TBODY)) {
    return (true);
  }
  return false;
};

Handsontable.TableView.prototype.isCellEdited = function () {
  var activeEditor = this.instance.getActiveEditor();
  return activeEditor && activeEditor.isOpened();
};

Handsontable.TableView.prototype.beforeRender = function (force) {
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'beforeRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.onDraw = function (force) {
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'afterRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.render = function () {
  this.wt.draw(!this.instance.forceFullRender);
  this.instance.forceFullRender = false;
//  this.instance.rootElement.triggerHandler('render.handsontable');
};

/**
 * Returns td object given coordinates
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.getCellAtCoords = function (coords) {
  var td = this.wt.wtTable.getCell(coords);
  if (td < 0) { //there was an exit code (cell is out of bounds)
    return null;
  }
  else {
    return td;
  }
};

/**
 * Scroll viewport to selection
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.scrollViewport = function (coords) {
  this.wt.scrollViewport(coords);
};

/**
 * Append row header to a TH element
 * @param row
 * @param TH
 */
Handsontable.TableView.prototype.appendRowHeader = function (row, TH) {
  var DIV = document.createElement('DIV'),
    SPAN = document.createElement('SPAN');

  DIV.className = 'relative';
  SPAN.className = 'rowHeader';

  if (row > -1) {
    Handsontable.Dom.fastInnerHTML(SPAN, this.instance.getRowHeader(row));
  } else {
    Handsontable.Dom.fastInnerText(SPAN, '\u00A0');
  }

  DIV.appendChild(SPAN);
  Handsontable.Dom.empty(TH);

  TH.appendChild(DIV);

  Handsontable.hooks.run(this.instance, 'afterGetRowHeader', row, TH);
};

/**
 * Append column header to a TH element
 * @param col
 * @param TH
 */
Handsontable.TableView.prototype.appendColHeader = function (col, TH) {
  var DIV = document.createElement('DIV')
    , SPAN = document.createElement('SPAN');

  DIV.className = 'relative';
  SPAN.className = 'colHeader';

  if (col > -1) {
    Handsontable.Dom.fastInnerHTML(SPAN, this.instance.getColHeader(col));
  } else {
    Handsontable.Dom.fastInnerText(SPAN, '\u00A0');
  }
  DIV.appendChild(SPAN);

  Handsontable.Dom.empty(TH);
  TH.appendChild(DIV);
  Handsontable.hooks.run(this.instance, 'afterGetColHeader', col, TH);
};

/**
 * Given a element's left position relative to the viewport, returns maximum element width until the right edge of the viewport (before scrollbar)
 * @param {Number} leftOffset
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementWidth = function (leftOffset) {
  var workspaceWidth = this.wt.wtViewport.getWorkspaceWidth();
  var maxWidth = workspaceWidth - leftOffset;
  return maxWidth > 0 ? maxWidth : 0;
};

/**
 * Given a element's top position relative to the viewport, returns maximum element height until the bottom edge of the viewport (before scrollbar)
 * @param {Number} topOffset
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementHeight = function (topOffset) {
  var workspaceHeight = this.wt.wtViewport.getWorkspaceHeight();
  var maxHeight = workspaceHeight - topOffset;
  return maxHeight > 0 ? maxHeight : 0;
};

Handsontable.TableView.prototype.mainViewIsActive = function () {
  return this.wt === this.activeWt;
};

Handsontable.TableView.prototype.destroy = function () {
  this.wt.destroy();
  this.eventManager.clear();
};
<|endoftext|>


<commit_before>var bms = bms || {};
var angular = angular || {};

(function() {
  bms = window.parent.bmsapi(window.frameElement.sessionId, window.frameElement.viewId);
  angular = window.parent.angular;
})();
<commit_msg>fixed template<commit_after>var bms = bms || {};
var angular = angular || {};

(function() {
  bms = window.parent.bmsapi(window.frameElement.getAttribute('sessionId'), window.frameElement.getAttribute('viewId'));
  angular = window.parent.angular;
})();
<|endoftext|>


<commit_before>var Group = require('../../db/models/group.js');
var User = require('../../db/models/user.js');
var util = require('../util.js');

module.exports = {
  findGroup: function (req, res, next) {
    var query = {};
    var field = req.params.prop;
    var value = req.params.query;
    if (field && value) query[field] = value;

    Group.find(query).exec()
      .then(function(group) {
        if (!group) {
          res.body = 'Group not found';
          util.sendResponse(req, res, 404);
        } else {
          res.body = group;
          util.sendResponse(req, res, 200);
        }
      })
      .catch(function(error) {
        console.log(error);
      });
  },
  createGroup: function (req, res, next) {
    var params = {
      name: req.body.name,
      members: req.body.members || [],
      recipes: req.body.recipes || []
    };

    Group.find(params).exec()
      .then(function(group) {
        console.log(group);
        if (group.length !== 0) {
          console.log('Group already exists');
          // fix response type
          util.sendResponse(req, res, 404);
        } else {
          Group.create(params)
            .then(function(created) {
              res.body = 'Group created';
              util.sendResponse(req, res, 201);
            });
        }
      });
  },
  updateGroup: function (req, res, next) {
    if (req.body.currentUser) {
      var user = util.decodeToken(req);
    }
    var groupId = req.body.groupId;
    Group.findOne({_id: groupId}).exec()
      .then(function(group) {
        group.members.push(user._id);
        group.save()
          .then(function(saved) {
            User.findOne({_id: user._id}).exec()
              .then(function(foundUser) {
                foundUser.groups.push(groupId);
                foundUser.save()
                  .then(function(saved) {
                    util.sendResponse(req, res, 200);
                  });
              });
          });
      });
  }
};<commit_msg>(fix) Fixed create group to return the created group<commit_after>var Group = require('../../db/models/group.js');
var User = require('../../db/models/user.js');
var util = require('../util.js');

module.exports = {
  findGroup: function (req, res, next) {
    var query = {};
    var field = req.params.prop;
    var value = req.params.query;
    if (field && value) query[field] = value;

    Group.find(query).exec()
      .then(function(group) {
        if (!group) {
          res.body = 'Group not found';
          util.sendResponse(req, res, 404);
        } else {
          res.body = group;
          util.sendResponse(req, res, 200);
        }
      })
      .catch(function(error) {
        console.log(error);
      });
  },
  createGroup: function (req, res, next) {
    var params = {
      name: req.body.name,
      members: req.body.members || [],
      recipes: req.body.recipes || []
    };

    Group.find(params).exec()
      .then(function(group) {
        console.log(group);
        if (group.length !== 0) {
          console.log('Group already exists');
          // fix response type
          util.sendResponse(req, res, 404);
        } else {
          Group.create(params)
            .then(function(createdGroup) {
              res.body = createdGroup;
              util.sendResponse(req, res, 201);
            });
        }
      });
  },
  updateGroup: function (req, res, next) {
    if (req.body.currentUser) {
      var user = util.decodeToken(req);
    }
    var groupId = req.body.groupId;
    Group.findOne({_id: groupId}).exec()
      .then(function(group) {
        group.members.push(user._id);
        group.save()
          .then(function(saved) {
            User.findOne({_id: user._id}).exec()
              .then(function(foundUser) {
                foundUser.groups.push(groupId);
                foundUser.save()
                  .then(function(saved) {
                    util.sendResponse(req, res, 200);
                  });
              });
          });
      });
  }
};<|endoftext|>


<commit_before>var PlaybackPlugin = require('../base/playback_plugin');

var HTML5VideoPlaybackPlugin = PlaybackPlugin.extend({
  events: {
    'timeupdate': 'timeUpdated',
    'ended': 'ended'
  },
  tagName: 'video',
  className: 'container',
  initialize: function(options) {
    this.el.src = options.src;
    this.settings = ['play', 'stop', 'pause', 'seekbar', 'volume'];
    this.container.settings = this.settings;
    this.listenTo(this.container, 'container:play', this.play);
    this.listenTo(this.container, 'container:pause', this.pause);
    this.listenTo(this.container, 'container:seek', this.seek);
    //this.listenTo(this.container, 'container:fullscreen', this.fullscreen);
    this.listenTo(this.container, 'container:volume', this.volume);
    this.render(); // it should render when the container trigger 'ready'
  },
  play: function() {
    this.el.play();
  },
  pause: function() {
    this.el.pause();
  },
  fullscreen: function() {
    //this is not right, the player goes fullscreen, not the playback.
    this.el.webkitRequestFullscreen();
  },
  volume: function(value) {
    this.el.volume = value / 100;
  },
  mute: function() {
    this.el.volume = 0;
  },
  unmute: function() {
    this.el.volume = 1;
  },
  isMuted: function() {
    return !!this.el.volume;
  },
  ended: function() {
    this.trigger('container:timeupdate', 0);
  },
  seek: function(seekBarValue) {
    var time = this.el.duration * (seekBarValue / 100);
    this.el.currentTime = time;
  },
  getCurrentTime: function() {
    return this.el.currentTime;
  },
  getDuration: function() {
    return this.el.duration;
  },
  timeUpdated: function() {
    var time = (100 / this.el.duration) * this.el.currentTime;
    this.container.timeUpdated(time);
  },
  render: function() {
    this.container.$el.append(this.el);
    return this;
  },
});

module.exports = HTML5VideoPlaybackPlugin;
<commit_msg>video playback plugin: add stop feature<commit_after>var PlaybackPlugin = require('../base/playback_plugin');

var HTML5VideoPlaybackPlugin = PlaybackPlugin.extend({
  events: {
    'timeupdate': 'timeUpdated',
    'ended': 'ended'
  },
  tagName: 'video',
  className: 'container',
  initialize: function(options) {
    this.el.src = options.src;
    this.settings = ['play', 'stop', 'pause', 'seekbar', 'volume'];
    this.container.settings = this.settings;
    this.listenTo(this.container, 'container:play', this.play);
    this.listenTo(this.container, 'container:pause', this.pause);
    this.listenTo(this.container, 'container:seek', this.seek);
    //this.listenTo(this.container, 'container:fullscreen', this.fullscreen);
    this.listenTo(this.container, 'container:volume', this.volume);
    this.listenTo(this.container, 'container:stop', this.stop);
    this.render(); // it should render when the container trigger 'ready'
  },
  play: function() {
    this.el.play();
  },
  pause: function() {
    this.el.pause();
  },
  stop: function() {
    this.pause();
    this.el.currentTime = 0;
  },
  fullscreen: function() {
    //this is not right, the player goes fullscreen, not the playback.
    this.el.webkitRequestFullscreen();
  },
  volume: function(value) {
    this.el.volume = value / 100;
  },
  mute: function() {
    this.el.volume = 0;
  },
  unmute: function() {
    this.el.volume = 1;
  },
  isMuted: function() {
    return !!this.el.volume;
  },
  ended: function() {
    this.trigger('container:timeupdate', 0);
  },
  seek: function(seekBarValue) {
    var time = this.el.duration * (seekBarValue / 100);
    this.el.currentTime = time;
  },
  getCurrentTime: function() {
    return this.el.currentTime;
  },
  getDuration: function() {
    return this.el.duration;
  },
  timeUpdated: function() {
    var time = (100 / this.el.duration) * this.el.currentTime;
    this.container.timeUpdated(time);
  },
  render: function() {
    this.container.$el.append(this.el);
    return this;
  },
});

module.exports = HTML5VideoPlaybackPlugin;
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2009 - 2010 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects
 * under both the MIT and GPL version 2.0 licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * The author would appreciate an email letting him know of any substantial
 * use of jqPlot.  You can reach the author at: chris dot leonello at gmail
 * dot com or see http://www.jqplot.com/info.php .  This is, of course,
 * not required.
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * Thanks for using jqPlot!
 *
 */
(function($) {
    /**
     * Class: $.jqplot.BlockRenderer
     * Plugin renderer to draw a x-y block chart.
     */
    $.jqplot.BlockRenderer = function(){
        $.jqplot.LineRenderer.call(this);
    };

    $.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer();
    $.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer;

    // called with scope of a series
    $.jqplot.BlockRenderer.prototype.init = function(options) {
        // Group: Properties
        //
        // prop: css
        // default css styles that will be applied to all data blocks.
        // these values will be overridden by css styles supplied with the
        // individulal data points.
        this.css = {padding:'2px', background:'#ddd', border:'1px solid #999', textAlign:'center'};
        // prop: clss
        // css class name applied to all data blocks in the series.
        this.clss = 'jqplot-series-block';
        // prop varyBlockColors
        // true to color each data block in this series differently, according
        // to the "seriesColors" array of this series.
        this.varyBlockColors = false;
        // prop: escapeHtml
        // true to escape html in the box label.
        this.escapeHtml = false;
        // prop: insertBreaks
        // true to turn spaces in data block label into html breaks
.
        this.insertBreaks = true;
        $.extend(true, this, options);
        this.canvas = new $.jqplot.BlockCanvas();
        this.shadowCanvas =  new $.jqplot.BlockCanvas();
        this.canvas._plotDimensions = this._plotDimensions;
        this.shadowCanvas._plotDimensions = this._plotDimensions;

        this.moveBlock = function (idx, x, y) {
            // update plotData, stackData, data and gridData
            // x and y are in data coordinates.
            var el = this.canvas._elem.children(':eq('+idx+')');
            this.data[idx][0] = x;
            this.data[idx][1] = y;
            this._plotData[idx][0] = x;
            this._plotData[idx][1] = y;
            this._stackData[idx][0] = x;
            this._stackData[idx][1] = y;
            this.gridData[idx][0] = this._xaxis.series_u2p(x);
            this.gridData[idx][1] = this._yaxis.series_u2p(y);
            var w = el.outerWidth();
            var h = el.outerHeight();
            var left = this.gridData[idx][0] - w/2 + 'px';
            var top = this.gridData[idx][1] - h/2 + 'px';
            el.css({left:left, top:top});
            el = null;
        };
    };

    // called with scope of series
    $.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) {
        var i, el, d, gd, t, css, w, h, left, top;
        var opts = (options != undefined) ? options : {};
        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
        this.canvas._elem.empty();
        for (i=0; i<this.gridData.length; i++) {
            d = this.data[i];
            gd = this.gridData[i];
            t = '';
            css = {};
            if (typeof d[2] == 'string') t = d[2];
            else if (typeof d[2] == 'object') css = d[2];
            if (typeof d[3] ==  'object') css = d[3];
            if (this.insertBreaks) t = t.replace(/ /g, '
');
            css = $.extend(true, {}, this.css, css);
            // create a div
            el = $('

');
            this.canvas._elem.append(el);
            // set text
            this.escapeHtml ? el.text(t) : el.html(t);
            // style it
            el.css(css);
            w = el.outerWidth();
            h = el.outerHeight();
            left = gd[0] - w/2 + 'px';
            top = gd[1] - h/2 + 'px';
            el.css({left:left, top:top});
            el = null;
        }
    };

    $.jqplot.BlockCanvas = function() {
        $.jqplot.ElemContainer.call(this);
        this._ctx;
    };

    $.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer();
    $.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas;

    $.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
        this._offsets = offsets;
        var klass = 'jqplot-blockCanvas';
        if (clss != undefined) {
            klass = clss;
        }
        var elem;
        // if this canvas already has a dom element, don't make a new one.
        if (this._elem) {
            elem = this._elem.get(0);
        }
        else {
            elem = document.createElement('div');
        }
        // if new plotDimensions supplied, use them.
        if (plotDimensions != undefined) {
            this._plotDimensions = plotDimensions;
        }

        var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px';
        var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px';
        this._elem = $(elem);
        this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top });

        this._elem.addClass(klass);
        return this._elem;
    };

    $.jqplot.BlockCanvas.prototype.setContext = function() {
        this._ctx = {
            canvas:{
                width:0,
                height:0
            },
            clearRect:function(){return null}
        };
        return this._ctx;
    };

})(jQuery);

    <commit_msg>Removed some unused properties. Made css specification more robust. Added docs.<commit_after>/**
 * Copyright (c) 2009 - 2010 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects
 * under both the MIT and GPL version 2.0 licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * The author would appreciate an email letting him know of any substantial
 * use of jqPlot.  You can reach the author at: chris dot leonello at gmail
 * dot com or see http://www.jqplot.com/info.php .  This is, of course,
 * not required.
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * Thanks for using jqPlot!
 *
 */
(function($) {
    /**
     * Class: $.jqplot.BlockRenderer
     * Plugin renderer to draw a x-y block chart.  A Block chart has data points displayed as
     * colored squares with a text label inside.  Data must be supplied in the form:
     *
     * > [[x1, y1, "label 1", {css}], [x2, y2, "label 2", {css}], ...]
     *
     * The label and css object are optional.  If the label is ommitted, the
     * box will collapse unless a css height and/or width is specified.
     *
     * The css object is an object specifying css properties
     * such as:
     *
     * > {background:'#4f98a5', border:'3px solid gray', padding:'1px'}
     *
     * Note that css properties specified with the data point override defaults
     * specified with the series.
     *
     */
    $.jqplot.BlockRenderer = function(){
        $.jqplot.LineRenderer.call(this);
    };

    $.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer();
    $.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer;

    // called with scope of a series
    $.jqplot.BlockRenderer.prototype.init = function(options) {
        // Group: Properties
        //
        // prop: css
        // default css styles that will be applied to all data blocks.
        // these values will be overridden by css styles supplied with the
        // individulal data points.
        this.css = {padding:'2px', border:'1px solid #999', textAlign:'center'};
        // prop: escapeHtml
        // true to escape html in the box label.
        this.escapeHtml = false;
        // prop: insertBreaks
        // true to turn spaces in data block label into html breaks
.
        this.insertBreaks = true;
        $.extend(true, this, options);
        this.canvas = new $.jqplot.BlockCanvas();
        this.shadowCanvas =  new $.jqplot.BlockCanvas();
        this.canvas._plotDimensions = this._plotDimensions;
        this.shadowCanvas._plotDimensions = this._plotDimensions;

        // group: Methods
        //
        // Method: moveBlock
        // Moves an individual block.  More efficient than redrawing
        // the whole series by calling plot.drawSeries().
        // Parameters:
        // idx - the 0 based index of the block or point in this series.
        // x - the x coordinate in data units (value on x axis) to move the block to.
        // y - the y coordinate in data units (value on the y axis) to move the block to.
        this.moveBlock = function (idx, x, y) {
            // update plotData, stackData, data and gridData
            // x and y are in data coordinates.
            var el = this.canvas._elem.children(':eq('+idx+')');
            this.data[idx][0] = x;
            this.data[idx][1] = y;
            this._plotData[idx][0] = x;
            this._plotData[idx][1] = y;
            this._stackData[idx][0] = x;
            this._stackData[idx][1] = y;
            this.gridData[idx][0] = this._xaxis.series_u2p(x);
            this.gridData[idx][1] = this._yaxis.series_u2p(y);
            var w = el.outerWidth();
            var h = el.outerHeight();
            var left = this.gridData[idx][0] - w/2 + 'px';
            var top = this.gridData[idx][1] - h/2 + 'px';
            el.css({left:left, top:top});
            el = null;
        };
    };

    // called with scope of series
    $.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) {
        var i, el, d, gd, t, css, w, h, left, top;
        var opts = (options != undefined) ? options : {};
        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
        this.canvas._elem.empty();
        for (i=0; i<this.gridData.length; i++) {
            d = this.data[i];
            gd = this.gridData[i];
            t = '';
            css = {};
            if (typeof d[2] == 'string') t = d[2];
            else if (typeof d[2] == 'object') css = d[2];
            if (typeof d[3] ==  'object') css = d[3];
            if (this.insertBreaks) t = t.replace(/ /g, '
');
            css = $.extend(true, {}, this.css, css);
            // create a div
            el = $('

');
            this.canvas._elem.append(el);
            // set text
            this.escapeHtml ? el.text(t) : el.html(t);
            // style it
            // remove styles we don't want overridden.
            delete css.position;
            delete css.marginRight;
            delete css.marginLeft;
            if (!css.background && !css.backgroundColor && !css.backgroundImage) css.background = colorGenerator.next();
            el.css(css);
            w = el.outerWidth();
            h = el.outerHeight();
            left = gd[0] - w/2 + 'px';
            top = gd[1] - h/2 + 'px';
            el.css({left:left, top:top});
            el = null;
        }
    };

    $.jqplot.BlockCanvas = function() {
        $.jqplot.ElemContainer.call(this);
        this._ctx;
    };

    $.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer();
    $.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas;

    $.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
        this._offsets = offsets;
        var klass = 'jqplot-blockCanvas';
        if (clss != undefined) {
            klass = clss;
        }
        var elem;
        // if this canvas already has a dom element, don't make a new one.
        if (this._elem) {
            elem = this._elem.get(0);
        }
        else {
            elem = document.createElement('div');
        }
        // if new plotDimensions supplied, use them.
        if (plotDimensions != undefined) {
            this._plotDimensions = plotDimensions;
        }

        var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px';
        var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px';
        this._elem = $(elem);
        this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top });

        this._elem.addClass(klass);
        return this._elem;
    };

    $.jqplot.BlockCanvas.prototype.setContext = function() {
        this._ctx = {
            canvas:{
                width:0,
                height:0
            },
            clearRect:function(){return null}
        };
        return this._ctx;
    };

})(jQuery);

    <|endoftext|>


<commit_before>/**
 * This file is where we will write tests for the
 * functions in update-job/script.js using mocha and selenium.
 * Running `npm test` in the command line will run all
 * the mocha tests in the walk-in-interview directory.
 * Also note that the Dev App Server should be running
 * using `mvn package appengine:run` before running the tests.
 * The tests can also be run by cd-ing into this directory and
 * running `mocha script_test.js` in the command line.
 * To get test report formatted nicely, cd into this folder and
 * run `mocha script_test.js --reporter mochawesome --reporter-options
 * autoOpen=true`.
 */

/**
 * Unable to make imports from other files.
 * TODO(issue/38): import getRequirementsList() here.
 */
const REQUIREMENTS_LIST = {
  'O_LEVEL': 'O Level',
  'LANGUAGE_ENGLISH': 'English',
  'DRIVING_LICENSE_C': 'Category C Driving License',
};

/**
 * Note that if `https` is used instead in the url below then
 * we get the following error: ERR_SSL_PROTOCOL_ERROR
 */
const JOBPAGE_URL = 'http://localhost:3000/update-job/index.html';
const HOMEPAGE_URL = 'http://localhost:3000/job-details/index.html';

const assert = require('chai').assert;
const webdriver = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const path = require('chromedriver').path;

const service = new chrome.ServiceBuilder(path).build();
chrome.setDefaultService(service);

const By = webdriver.By;
const until = webdriver.until;
const options = new chrome.Options();

/**
 * Note that these tests are headless.
 * To see the test browser pop up, remove this line.
 */
options.addArguments('headless');

/**
 * Set timeout to 50s so tests all have a chance to run.
 */
const TIMEOUT_MILLISECONDS = 50000;

let driver;

/** Note that this.timeout() will not work with arrow functions. */
describe('Update Job Tests', function() {
  this.timeout(TIMEOUT_MILLISECONDS);

  before(() => {
    driver = new webdriver.Builder().setChromeOptions(options)
        .withCapabilities(webdriver.Capabilities.chrome())
        .build();
    return driver.get(JOBPAGE_URL);
  });

  after(() => {
    return driver.quit();
  });

  describe('Page Rendering Tests', () => {
    describe('Page Title', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('page-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Update Job Post');
            });
      });
    });

    describe('Cancel Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Cancel');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'reset');
            });
      });
    });

    describe('Submit Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Update');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'submit');
            });
      });
    });

    describe('Error Message', () => {
      it('checks initially empty', () => {
        return driver.findElement(By.id('error-message'))
            .getText()
            .then((msg) => {
              assert.isEmpty(msg);
            });
      });
    });

    describe('Job Title', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Software Engineer');
            });
      });
    });

    describe('Job Description', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'textarea');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Fight to defeat hair line receding');
            });
      });
    });

    describe('Job Address', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Maple Tree');
            });
      });

      describe('Postal Code', () => {
        it('checks the type attribute', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('type')
              .then((value) => {
                assert.equal(value, 'text');
              });
        });

        it('checks the default value text', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('value')
              .then((value) => {
                assert.equal(value, '123');
              });
        });
      });
    });

    describe('Job Requirements', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('requirements-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Requirements');
            });
      });

      it('checks correct requirement list rendered', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              const list = REQUIREMENTS_LIST;
              for (const key in list) {
                if (list.hasOwnProperty(key)) {
                  if (!text.includes(list[key])) {
                    assert.fail(list[key] + ' requirement not included');
                  }
                }
              }
            });
      });

      it('checks the default option properly ticked', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              for (const requirement in text) {
                if (requirement === 'O Level') {
                  requirement.getAttribute('checked')
                  .then((checked) => {
                    assert.isTrue(checked);
                  })
                }

                if (requirement === 'English') {
                  requirement.getAttribute('checked')
                  .then((checked) => {
                    assert.isTrue(checked);
                  })
                }
              }
            });
      });
    });

    describe('Job Pay', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('pay-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Pay');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getText()
            .then((text) => {
              const options = {
                'HOURLY': 'Hourly',
                'WEEKLY': 'Weekly',
                'MONTHLY': 'Monthly',
                'YEARLY': 'Yearly',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'MONTHLY');
            });
      });

      it('min: checks the type attribute', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('min: checks the default value text', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '1');
            });
      });

      it('max: checks the type attribute', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('max: checks the default value text', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '4');
            });
      });
    });

    describe('Job Duration', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('duration-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Duration');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('duration'))
            .getText()
            .then((text) => {
              const options = {
                'ONE_WEEK': '1 Week',
                'TWO_WEEKS': '2 Weeks',
                'ONE_MONTH': '1 Month',
                'SIX_MONTHS': '6 Months',
                'ONE_YEAR': '1 Year',
                'OTHER': 'Other',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('duration'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'ONE_WEEK');
            });
      });
    });

    describe('Job Expiry', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('expiry-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Expiry');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'date');
            });
      });

      it('checks the min attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('min')
            .then((value) => {
              const expected = new Date().toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks the max attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('max')
            .then((value) => {
              const date = new Date();
              date.setFullYear(date.getFullYear() + 1);
              const expected = date.toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '2020-10-05');
            });
      });
    });
  });

  describe('Page Functionality Tests', () => {
    // TODO(issue/63): add more tests for invalid input.
    beforeEach(() => {
      /**
       * For the functionality tests, since we are testing the
       * cancel/submit buttons, the page url may have changed
       * in the test.
       * Always resets the current page to the job page
       * for the rest of the tests.
       */
        return driver.get(JOBPAGE_URL);
    });

    describe('Cancel Button', () => {
      /**
       * Clicking the cancel button should return the user to the homepage
       * and not make any POST request.
       */
      it('should return to homepage', () => {
        return clickCancel(driver)
            .then(() => driver.wait(until.urlIs(HOMEPAGE_URL)))
            .then(() => driver.getCurrentUrl())
            .then((currUrl) => assert.equal(currUrl, HOMEPAGE_URL));
      });
    });

    /**
     * If a field is not valid, then clicking submit will display an error
     * message with the invalid field, and no POST request will be made.
     */
    describe('Submit Button', () => {
      const date = new Date();
      const today = `${(date.getMonth() + 1)}-${date.getDate()}` +
        `-${date.getFullYear()}`;

      // Since all fields should be pre-filled with existing job post,
      // there is no need to add valid inputs to all fields again.

      it('no error message for all valid inputs', () => {
        return clickUpdate(driver)
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text, ''));
      });

      it('no job title', () => {
        /**
         * The job title must be cleared here to test it.
         */
        return driver.findElement(By.id('title')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Title'));
      });

      it('incorrect job address format', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('incorrect postal code', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('min greater than max', () => {
        /**
         * The pay-min and pay-max fields must be cleared before sending
         * another key.
         */
        return driver.findElement(By.id('pay-min')).clear()
            .then(() => driver.findElement(By.id('pay-max')).clear())
            .then(() => driver.findElement(By.id('pay-min')).sendKeys('7'))
            .then(() => driver.findElement(By.id('pay-max')).sendKeys('6'))
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Pay'));
        });

      it('expiry date not chosen', () => {
        return driver.findElement(By.id('expiry')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Expiry'));
      });

      /**
       * If all the fields(including jobId) are valid, then a POST request should be made and the
       * user should be returned to the homepage.
       * However, since so far cloud firestore cannot be set up locally
       * and only a dummy jobId is passed, it can only catch an error.
       */
      it('not update successfully', () => {
        return clickUpdate(driver)
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There was an error while storing the job post. Please try again'));
      });

      /**
       * TODO(issue/40): check that POST request has been made
       * & also check that the response is what was expected
       */
    });
  });
});

/**
 * This function just to submit the updated job with the
 * filled out fields.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickUpdate(driver) {
  return driver.findElement(By.id('update')).click();
};

/**
 * This function just to click the cancel button and return
 * to the homepage.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickCancel(driver) {
  return driver.findElement(By.id('cancel')).click();
};
<commit_msg>Delete "all valid" test<commit_after>/**
 * This file is where we will write tests for the
 * functions in update-job/script.js using mocha and selenium.
 * Running `npm test` in the command line will run all
 * the mocha tests in the walk-in-interview directory.
 * Also note that the Dev App Server should be running
 * using `mvn package appengine:run` before running the tests.
 * The tests can also be run by cd-ing into this directory and
 * running `mocha script_test.js` in the command line.
 * To get test report formatted nicely, cd into this folder and
 * run `mocha script_test.js --reporter mochawesome --reporter-options
 * autoOpen=true`.
 */

/**
 * Unable to make imports from other files.
 * TODO(issue/38): import getRequirementsList() here.
 */
const REQUIREMENTS_LIST = {
  'O_LEVEL': 'O Level',
  'LANGUAGE_ENGLISH': 'English',
  'DRIVING_LICENSE_C': 'Category C Driving License',
};

/**
 * Note that if `https` is used instead in the url below then
 * we get the following error: ERR_SSL_PROTOCOL_ERROR
 */
const JOBPAGE_URL = 'http://localhost:3000/update-job/index.html';
const HOMEPAGE_URL = 'http://localhost:3000/job-details/index.html';

const assert = require('chai').assert;
const webdriver = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const path = require('chromedriver').path;

const service = new chrome.ServiceBuilder(path).build();
chrome.setDefaultService(service);

const By = webdriver.By;
const until = webdriver.until;
const options = new chrome.Options();

/**
 * Note that these tests are headless.
 * To see the test browser pop up, remove this line.
 */
options.addArguments('headless');

/**
 * Set timeout to 50s so tests all have a chance to run.
 */
const TIMEOUT_MILLISECONDS = 50000;

let driver;

/** Note that this.timeout() will not work with arrow functions. */
describe('Update Job Tests', function() {
  this.timeout(TIMEOUT_MILLISECONDS);

  before(() => {
    driver = new webdriver.Builder().setChromeOptions(options)
        .withCapabilities(webdriver.Capabilities.chrome())
        .build();
    return driver.get(JOBPAGE_URL);
  });

  after(() => {
    return driver.quit();
  });

  describe('Page Rendering Tests', () => {
    describe('Page Title', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('page-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Update Job Post');
            });
      });
    });

    describe('Cancel Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Cancel');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'reset');
            });
      });
    });

    describe('Submit Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Update');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'submit');
            });
      });
    });

    describe('Error Message', () => {
      it('checks initially empty', () => {
        return driver.findElement(By.id('error-message'))
            .getText()
            .then((msg) => {
              assert.isEmpty(msg);
            });
      });
    });

    describe('Job Title', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Software Engineer');
            });
      });
    });

    describe('Job Description', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'textarea');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Fight to defeat hair line receding');
            });
      });
    });

    describe('Job Address', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Maple Tree');
            });
      });

      describe('Postal Code', () => {
        it('checks the type attribute', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('type')
              .then((value) => {
                assert.equal(value, 'text');
              });
        });

        it('checks the default value text', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('value')
              .then((value) => {
                assert.equal(value, '123');
              });
        });
      });
    });

    describe('Job Requirements', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('requirements-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Requirements');
            });
      });

      it('checks correct requirement list rendered', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              const list = REQUIREMENTS_LIST;
              for (const key in list) {
                if (list.hasOwnProperty(key)) {
                  if (!text.includes(list[key])) {
                    assert.fail(list[key] + ' requirement not included');
                  }
                }
              }
            });
      });

      it('checks the default option properly ticked', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              for (const requirement in text) {
                if (requirement === 'O Level') {
                  requirement.getAttribute('checked')
                  .then((checked) => {
                    assert.isTrue(checked);
                  })
                }

                if (requirement === 'English') {
                  requirement.getAttribute('checked')
                  .then((checked) => {
                    assert.isTrue(checked);
                  })
                }
              }
            });
      });
    });

    describe('Job Pay', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('pay-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Pay');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getText()
            .then((text) => {
              const options = {
                'HOURLY': 'Hourly',
                'WEEKLY': 'Weekly',
                'MONTHLY': 'Monthly',
                'YEARLY': 'Yearly',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'MONTHLY');
            });
      });

      it('min: checks the type attribute', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('min: checks the default value text', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '1');
            });
      });

      it('max: checks the type attribute', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('max: checks the default value text', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '4');
            });
      });
    });

    describe('Job Duration', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('duration-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Duration');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('duration'))
            .getText()
            .then((text) => {
              const options = {
                'ONE_WEEK': '1 Week',
                'TWO_WEEKS': '2 Weeks',
                'ONE_MONTH': '1 Month',
                'SIX_MONTHS': '6 Months',
                'ONE_YEAR': '1 Year',
                'OTHER': 'Other',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('duration'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'ONE_WEEK');
            });
      });
    });

    describe('Job Expiry', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('expiry-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Expiry');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'date');
            });
      });

      it('checks the min attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('min')
            .then((value) => {
              const expected = new Date().toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks the max attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('max')
            .then((value) => {
              const date = new Date();
              date.setFullYear(date.getFullYear() + 1);
              const expected = date.toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '2020-10-05');
            });
      });
    });
  });

  describe('Page Functionality Tests', () => {
    // TODO(issue/63): add more tests for invalid input.
    beforeEach(() => {
      /**
       * For the functionality tests, since we are testing the
       * cancel/submit buttons, the page url may have changed
       * in the test.
       * Always resets the current page to the job page
       * for the rest of the tests.
       */
        return driver.get(JOBPAGE_URL);
    });

    describe('Cancel Button', () => {
      /**
       * Clicking the cancel button should return the user to the homepage
       * and not make any POST request.
       */
      it('should return to homepage', () => {
        return clickCancel(driver)
            .then(() => driver.wait(until.urlIs(HOMEPAGE_URL)))
            .then(() => driver.getCurrentUrl())
            .then((currUrl) => assert.equal(currUrl, HOMEPAGE_URL));
      });
    });

    /**
     * If a field is not valid, then clicking submit will display an error
     * message with the invalid field, and no POST request will be made.
     */
    describe('Submit Button', () => {
      const date = new Date();
      const today = `${(date.getMonth() + 1)}-${date.getDate()}` +
        `-${date.getFullYear()}`;

      // Since all fields should be pre-filled with existing job post,
      // there is no need to add valid inputs to all fields again.

      it('no job title', () => {
        /**
         * The job title must be cleared here to test it.
         */
        return driver.findElement(By.id('title')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Title'));
      });

      it('incorrect job address format', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('incorrect postal code', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('min greater than max', () => {
        /**
         * The pay-min and pay-max fields must be cleared before sending
         * another key.
         */
        return driver.findElement(By.id('pay-min')).clear()
            .then(() => driver.findElement(By.id('pay-max')).clear())
            .then(() => driver.findElement(By.id('pay-min')).sendKeys('7'))
            .then(() => driver.findElement(By.id('pay-max')).sendKeys('6'))
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Pay'));
        });

      it('expiry date not chosen', () => {
        return driver.findElement(By.id('expiry')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Expiry'));
      });

      /**
       * If all the fields(including jobId) are valid, then a POST request should be made and the
       * user should be returned to the homepage.
       * However, since so far cloud firestore cannot be set up locally
       * and only a dummy jobId is passed, it can only catch an error.
       */
      it('not update successfully', () => {
        return clickUpdate(driver)
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There was an error while storing the job post. Please try again'));
      });

      /**
       * TODO(issue/40): check that POST request has been made
       * & also check that the response is what was expected
       */
    });
  });
});

/**
 * This function just to submit the updated job with the
 * filled out fields.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickUpdate(driver) {
  return driver.findElement(By.id('update')).click();
};

/**
 * This function just to click the cancel button and return
 * to the homepage.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickCancel(driver) {
  return driver.findElement(By.id('cancel')).click();
};
<|endoftext|>