<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2017 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Layout code for a vertical variant of the flyout.
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.provide('Blockly.VerticalFlyout');

goog.require('Blockly.Block');
goog.require('Blockly.Events');
goog.require('Blockly.Flyout');
goog.require('Blockly.FlyoutButton');
goog.require('Blockly.utils');
goog.require('Blockly.WorkspaceSvg');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.math.Rect');
goog.require('goog.userAgent');

/**
 * Class for a flyout.
 * @param {!Object} workspaceOptions Dictionary of options for the workspace.
 * @extends {Blockly.Flyout}
 * @constructor
 */
Blockly.VerticalFlyout = function(workspaceOptions) {
  workspaceOptions.getMetrics = this.getMetrics_.bind(this);
  workspaceOptions.setMetrics = this.setMetrics_.bind(this);

  Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
  /**
   * Flyout should be laid out vertically.
   * @type {boolean}
   * @private
   */
  this.horizontalLayout_ = false;
};
goog.inherits(Blockly.VerticalFlyout, Blockly.Flyout);

/**
 * Return an object with all the metrics required to size scrollbars for the
 * flyout.  The following properties are computed:
 * .viewHeight: Height of the visible rectangle,
 * .viewWidth: Width of the visible rectangle,
 * .contentHeight: Height of the contents,
 * .contentWidth: Width of the contents,
 * .viewTop: Offset of top edge of visible rectangle from parent,
 * .contentTop: Offset of the top-most content from the y=0 coordinate,
 * .absoluteTop: Top-edge of view.
 * .viewLeft: Offset of the left edge of visible rectangle from parent,
 * .contentLeft: Offset of the left-most content from the x=0 coordinate,
 * .absoluteLeft: Left-edge of view.
 * @return {Object} Contains size and position metrics of the flyout.
 * @private
 */
Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
  if (!this.isVisible()) {
    // Flyout is hidden.
    return null;
  }

  try {
    var optionBox = this.workspace_.getCanvas().getBBox();
  } catch (e) {
    // Firefox has trouble with hidden elements (Bug 528969).
    var optionBox = {height: 0, y: 0, width: 0, x: 0};
  }

  // Padding for the end of the scrollbar.
  var absoluteTop = this.SCROLLBAR_PADDING;
  var absoluteLeft = 0;

  var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING;
  var viewWidth = this.width_;
  if (!this.RTL) {
    viewWidth -= this.SCROLLBAR_PADDING;
  }

  var metrics = {
    viewHeight: viewHeight,
    viewWidth: viewWidth,
    contentHeight: optionBox.height * this.workspace_.scale + 2 * this.MARGIN,
    contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN,
    viewTop: -this.workspace_.scrollY + optionBox.y,
    viewLeft: -this.workspace_.scrollX,
    contentTop: optionBox.y,
    contentLeft: optionBox.x,
    absoluteTop: absoluteTop,
    absoluteLeft: absoluteLeft
  };
  return metrics;
};

/**
 * Sets the translation of the flyout to match the scrollbars.
 * @param {!Object} xyRatio Contains a y property which is a float
 *     between 0 and 1 specifying the degree of scrolling and a
 *     similar x property.
 * @private
 */
Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
  var metrics = this.getMetrics_();
  // This is a fix to an apparent race condition.
  if (!metrics) {
    return;
  }
  if (goog.isNumber(xyRatio.y)) {
    this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y;
  }
  this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft,
      this.workspace_.scrollY + metrics.absoluteTop);
};

/**
 * Move the flyout to the edge of the workspace.
 */
Blockly.VerticalFlyout.prototype.position = function() {
  if (!this.isVisible()) {
    return;
  }
  var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
  if (!targetWorkspaceMetrics) {
    // Hidden components will return null.
    return;
  }
  // Record the height for Blockly.Flyout.getMetrics_
  this.height_ = targetWorkspaceMetrics.viewHeight;

  var edgeWidth = this.width_ - this.CORNER_RADIUS;
  var edgeHeight = targetWorkspaceMetrics.viewHeight - 2 * this.CORNER_RADIUS;
  this.setBackgroundPath_(edgeWidth, edgeHeight);

  var y = targetWorkspaceMetrics.absoluteTop;
  var x = targetWorkspaceMetrics.absoluteLeft;
  if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
    x += (targetWorkspaceMetrics.viewWidth - this.width_);
  }
  this.positionAt_(this.width_, this.height_, x, y);
};

/**
 * Create and set the path for the visible boundaries of the flyout.
 * @param {number} width The width of the flyout, not including the
 *     rounded corners.
 * @param {number} height The height of the flyout, not including
 *     rounded corners.
 * @private
 */
Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
  var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT;
  var totalWidth = width + this.CORNER_RADIUS;

  // Decide whether to start on the left or right.
  var path = ['M ' + (atRight ? totalWidth : 0) + ',0'];
  // Top.
  path.push('h', atRight ? -width : width);
  // Rounded corner.
  path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
      atRight ? 0 : 1,
      atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS,
      this.CORNER_RADIUS);
  // Side closest to workspace.
  path.push('v', Math.max(0, height));
  // Rounded corner.
  path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
      atRight ? 0 : 1,
      atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS,
      this.CORNER_RADIUS);
  // Bottom.
  path.push('h', atRight ? width : -width);
  path.push('z');
  this.svgBackground_.setAttribute('d', path.join(' '));
};

/**
 * Scroll the flyout to the top.
 */
Blockly.VerticalFlyout.prototype.scrollToStart = function() {
  this.scrollbar_.set(0);
};

/**
 * Scroll the flyout.
 * @param {!Event} e Mouse wheel scroll event.
 * @private
 */
Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
  var delta = e.deltaY;

  if (delta) {
    if (goog.userAgent.GECKO) {
      // Firefox's deltas are a tenth that of Chrome/Safari.
      delta *= 10;
    }
    var metrics = this.getMetrics_();
    var pos = (metrics.viewTop - metrics.contentTop) + delta;
    var limit = metrics.contentHeight - metrics.viewHeight;
    pos = Math.min(pos, limit);
    pos = Math.max(pos, 0);
    this.scrollbar_.set(pos);
    // When the flyout moves from a wheel event, hide WidgetDiv.
    Blockly.WidgetDiv.hide();
  }

  // Don't scroll the page.
  e.preventDefault();
  // Don't propagate mousewheel event (zooming).
  e.stopPropagation();
};

/**
 * Lay out the blocks in the flyout.
 * @param {!Array.<!Object>} contents The blocks and buttons to lay out.
 * @param {!Array.<number>} gaps The visible gaps between blocks.
 * @private
 */
Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
  this.workspace_.scale = this.targetWorkspace_.scale;
  var margin = this.MARGIN;
  var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH;
  var cursorY = margin;

  for (var i = 0, item; item = contents[i]; i++) {
    if (item.type == 'block') {
      var block = item.block;
      var allBlocks = block.getDescendants();
      for (var j = 0, child; child = allBlocks[j]; j++) {
        // Mark blocks as being inside a flyout.  This is used to detect and
        // prevent the closure of the flyout if the user right-clicks on such a
        // block.
        child.isInFlyout = true;
      }
      block.render();
      var root = block.getSvgRoot();
      var blockHW = block.getHeightWidth();
      block.moveBy(cursorX, cursorY);

      var rect = this.createRect_(block,
          this.RTL ? cursorX - blockHW.width : cursorX, cursorY, blockHW, i);

      this.addBlockListeners_(root, block, rect);

      cursorY += blockHW.height + gaps[i];
    } else if (item.type == 'button') {
      this.initFlyoutButton_(item.button, cursorX, cursorY);
      cursorY += item.button.height + gaps[i];
    }
  }
};

/**
 * Determine if a drag delta is toward the workspace, based on the position
 * and orientation of the flyout. This is used in determineDragIntention_ to
 * determine if a new block should be created or if the flyout should scroll.
 * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
 *     moved from the position at mouse down, in pixel units.
 * @return {boolean} true if the drag is toward the workspace.
 * @package
 */
Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(
    currentDragDeltaXY) {
  var dx = currentDragDeltaXY.x;
  var dy = currentDragDeltaXY.y;
  // Direction goes from -180 to 180, with 0 toward the right and 90 on top.
  var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;

  var range = this.dragAngleRange_;
  // Check for left or right dragging.
  if ((dragDirection < range && dragDirection > -range) ||
      (dragDirection < -180 + range || dragDirection > 180 - range)) {
    return true;
  }
  return false;
};

/**
 * Return the deletion rectangle for this flyout in viewport coordinates.
 * @return {goog.math.Rect} Rectangle in which to delete.
 */
Blockly.VerticalFlyout.prototype.getClientRect = function() {
  if (!this.svgGroup_) {
    return null;
  }

  var flyoutRect = this.svgGroup_.getBoundingClientRect();
  // BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
  // area are still deleted.  Must be larger than the largest screen size,
  // but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
  var BIG_NUM = 1000000000;
  var x = flyoutRect.left;
  var width = flyoutRect.width;

  if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
    return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width,
        BIG_NUM * 2);
  } else {  // Right
    return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, BIG_NUM * 2);
  }
};

/**
 * Compute width of flyout.  Position mat under each block.
 * For RTL: Lay out the blocks and buttons to be right-aligned.
 * @private
 */
Blockly.VerticalFlyout.prototype.reflowInternal_ = function() {
  this.workspace_.scale = this.targetWorkspace_.scale;
  var flyoutWidth = 0;
  var blocks = this.workspace_.getTopBlocks(false);
  for (var i = 0, block; block = blocks[i]; i++) {
    var width = block.getHeightWidth().width;
    if (block.outputConnection) {
      width -= Blockly.BlockSvg.TAB_WIDTH;
    }
    flyoutWidth = Math.max(flyoutWidth, width);
  }
  for (var i = 0, button; button = this.buttons_[i]; i++) {
    flyoutWidth = Math.max(flyoutWidth, button.width);
  }
  flyoutWidth += this.MARGIN * 1.5 + Blockly.BlockSvg.TAB_WIDTH;
  flyoutWidth *= this.workspace_.scale;
  flyoutWidth += Blockly.Scrollbar.scrollbarThickness;

  if (this.width_ != flyoutWidth) {
    for (var i = 0, block; block = blocks[i]; i++) {
      if (this.RTL) {
        // With the flyoutWidth known, right-align the blocks.
        var oldX = block.getRelativeToSurfaceXY().x;
        var newX = flyoutWidth / this.workspace_.scale - this.MARGIN -
            Blockly.BlockSvg.TAB_WIDTH;
        block.moveBy(newX - oldX, 0);
      }
      if (block.flyoutRect_) {
        this.moveRectToBlock_(block.flyoutRect_, block);
      }
    }
    if (this.RTL) {
      // With the flyoutWidth known, right-align the buttons.
      for (var i = 0, button; button = this.buttons_[i]; i++) {
        var y = button.getPosition().y;
        var x = flyoutWidth / this.workspace_.scale - button.width - this.MARGIN -
            Blockly.BlockSvg.TAB_WIDTH;
        button.moveTo(x, y);
      }
    }
    // Record the width for .getMetrics_ and .position.
    this.width_ = flyoutWidth;
    // Call this since it is possible the trash and zoom buttons need
    // to move. e.g. on a bottom positioned flyout when zoom is clicked.
    this.targetWorkspace_.resize();
  }
};
<commit_msg>Work around a problem with RTL mutators<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2017 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Layout code for a vertical variant of the flyout.
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.provide('Blockly.VerticalFlyout');

goog.require('Blockly.Block');
goog.require('Blockly.Events');
goog.require('Blockly.Flyout');
goog.require('Blockly.FlyoutButton');
goog.require('Blockly.utils');
goog.require('Blockly.WorkspaceSvg');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.math.Rect');
goog.require('goog.userAgent');

/**
 * Class for a flyout.
 * @param {!Object} workspaceOptions Dictionary of options for the workspace.
 * @extends {Blockly.Flyout}
 * @constructor
 */
Blockly.VerticalFlyout = function(workspaceOptions) {
  workspaceOptions.getMetrics = this.getMetrics_.bind(this);
  workspaceOptions.setMetrics = this.setMetrics_.bind(this);

  Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
  /**
   * Flyout should be laid out vertically.
   * @type {boolean}
   * @private
   */
  this.horizontalLayout_ = false;
};
goog.inherits(Blockly.VerticalFlyout, Blockly.Flyout);

/**
 * Return an object with all the metrics required to size scrollbars for the
 * flyout.  The following properties are computed:
 * .viewHeight: Height of the visible rectangle,
 * .viewWidth: Width of the visible rectangle,
 * .contentHeight: Height of the contents,
 * .contentWidth: Width of the contents,
 * .viewTop: Offset of top edge of visible rectangle from parent,
 * .contentTop: Offset of the top-most content from the y=0 coordinate,
 * .absoluteTop: Top-edge of view.
 * .viewLeft: Offset of the left edge of visible rectangle from parent,
 * .contentLeft: Offset of the left-most content from the x=0 coordinate,
 * .absoluteLeft: Left-edge of view.
 * @return {Object} Contains size and position metrics of the flyout.
 * @private
 */
Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
  if (!this.isVisible()) {
    // Flyout is hidden.
    return null;
  }

  try {
    var optionBox = this.workspace_.getCanvas().getBBox();
  } catch (e) {
    // Firefox has trouble with hidden elements (Bug 528969).
    var optionBox = {height: 0, y: 0, width: 0, x: 0};
  }

  // Padding for the end of the scrollbar.
  var absoluteTop = this.SCROLLBAR_PADDING;
  var absoluteLeft = 0;

  var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING;
  var viewWidth = this.width_;
  if (!this.RTL) {
    viewWidth -= this.SCROLLBAR_PADDING;
  }

  var metrics = {
    viewHeight: viewHeight,
    viewWidth: viewWidth,
    contentHeight: optionBox.height * this.workspace_.scale + 2 * this.MARGIN,
    contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN,
    viewTop: -this.workspace_.scrollY + optionBox.y,
    viewLeft: -this.workspace_.scrollX,
    contentTop: optionBox.y,
    contentLeft: optionBox.x,
    absoluteTop: absoluteTop,
    absoluteLeft: absoluteLeft
  };
  return metrics;
};

/**
 * Sets the translation of the flyout to match the scrollbars.
 * @param {!Object} xyRatio Contains a y property which is a float
 *     between 0 and 1 specifying the degree of scrolling and a
 *     similar x property.
 * @private
 */
Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
  var metrics = this.getMetrics_();
  // This is a fix to an apparent race condition.
  if (!metrics) {
    return;
  }
  if (goog.isNumber(xyRatio.y)) {
    this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y;
  }
  this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft,
      this.workspace_.scrollY + metrics.absoluteTop);
};

/**
 * Move the flyout to the edge of the workspace.
 */
Blockly.VerticalFlyout.prototype.position = function() {
  if (!this.isVisible()) {
    return;
  }
  var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
  if (!targetWorkspaceMetrics) {
    // Hidden components will return null.
    return;
  }
  // Record the height for Blockly.Flyout.getMetrics_
  this.height_ = targetWorkspaceMetrics.viewHeight;

  var edgeWidth = this.width_ - this.CORNER_RADIUS;
  var edgeHeight = targetWorkspaceMetrics.viewHeight - 2 * this.CORNER_RADIUS;
  this.setBackgroundPath_(edgeWidth, edgeHeight);

  var y = targetWorkspaceMetrics.absoluteTop;
  var x = targetWorkspaceMetrics.absoluteLeft;
  if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
    x += (targetWorkspaceMetrics.viewWidth - this.width_);
    // Save the location of the left edge of the flyout, for use when firefox
    // gets the bounding client rect wrong.
    this.leftEdge_ = x;
  }
  this.positionAt_(this.width_, this.height_, x, y);

};

/**
 * Create and set the path for the visible boundaries of the flyout.
 * @param {number} width The width of the flyout, not including the
 *     rounded corners.
 * @param {number} height The height of the flyout, not including
 *     rounded corners.
 * @private
 */
Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
  var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT;
  var totalWidth = width + this.CORNER_RADIUS;

  // Decide whether to start on the left or right.
  var path = ['M ' + (atRight ? totalWidth : 0) + ',0'];
  // Top.
  path.push('h', atRight ? -width : width);
  // Rounded corner.
  path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
      atRight ? 0 : 1,
      atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS,
      this.CORNER_RADIUS);
  // Side closest to workspace.
  path.push('v', Math.max(0, height));
  // Rounded corner.
  path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
      atRight ? 0 : 1,
      atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS,
      this.CORNER_RADIUS);
  // Bottom.
  path.push('h', atRight ? width : -width);
  path.push('z');
  this.svgBackground_.setAttribute('d', path.join(' '));
};

/**
 * Scroll the flyout to the top.
 */
Blockly.VerticalFlyout.prototype.scrollToStart = function() {
  this.scrollbar_.set(0);
};

/**
 * Scroll the flyout.
 * @param {!Event} e Mouse wheel scroll event.
 * @private
 */
Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
  var delta = e.deltaY;

  if (delta) {
    if (goog.userAgent.GECKO) {
      // Firefox's deltas are a tenth that of Chrome/Safari.
      delta *= 10;
    }
    var metrics = this.getMetrics_();
    var pos = (metrics.viewTop - metrics.contentTop) + delta;
    var limit = metrics.contentHeight - metrics.viewHeight;
    pos = Math.min(pos, limit);
    pos = Math.max(pos, 0);
    this.scrollbar_.set(pos);
    // When the flyout moves from a wheel event, hide WidgetDiv.
    Blockly.WidgetDiv.hide();
  }

  // Don't scroll the page.
  e.preventDefault();
  // Don't propagate mousewheel event (zooming).
  e.stopPropagation();
};

/**
 * Lay out the blocks in the flyout.
 * @param {!Array.<!Object>} contents The blocks and buttons to lay out.
 * @param {!Array.<number>} gaps The visible gaps between blocks.
 * @private
 */
Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
  this.workspace_.scale = this.targetWorkspace_.scale;
  var margin = this.MARGIN;
  var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH;
  var cursorY = margin;

  for (var i = 0, item; item = contents[i]; i++) {
    if (item.type == 'block') {
      var block = item.block;
      var allBlocks = block.getDescendants();
      for (var j = 0, child; child = allBlocks[j]; j++) {
        // Mark blocks as being inside a flyout.  This is used to detect and
        // prevent the closure of the flyout if the user right-clicks on such a
        // block.
        child.isInFlyout = true;
      }
      block.render();
      var root = block.getSvgRoot();
      var blockHW = block.getHeightWidth();
      block.moveBy(cursorX, cursorY);

      var rect = this.createRect_(block,
          this.RTL ? cursorX - blockHW.width : cursorX, cursorY, blockHW, i);

      this.addBlockListeners_(root, block, rect);

      cursorY += blockHW.height + gaps[i];
    } else if (item.type == 'button') {
      this.initFlyoutButton_(item.button, cursorX, cursorY);
      cursorY += item.button.height + gaps[i];
    }
  }
};

/**
 * Determine if a drag delta is toward the workspace, based on the position
 * and orientation of the flyout. This is used in determineDragIntention_ to
 * determine if a new block should be created or if the flyout should scroll.
 * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
 *     moved from the position at mouse down, in pixel units.
 * @return {boolean} true if the drag is toward the workspace.
 * @package
 */
Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(
    currentDragDeltaXY) {
  var dx = currentDragDeltaXY.x;
  var dy = currentDragDeltaXY.y;
  // Direction goes from -180 to 180, with 0 toward the right and 90 on top.
  var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;

  var range = this.dragAngleRange_;
  // Check for left or right dragging.
  if ((dragDirection < range && dragDirection > -range) ||
      (dragDirection < -180 + range || dragDirection > 180 - range)) {
    return true;
  }
  return false;
};

/**
 * Return the deletion rectangle for this flyout in viewport coordinates.
 * @return {goog.math.Rect} Rectangle in which to delete.
 */
Blockly.VerticalFlyout.prototype.getClientRect = function() {
  if (!this.svgGroup_) {
    return null;
  }

  var flyoutRect = this.svgGroup_.getBoundingClientRect();
  // BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
  // area are still deleted.  Must be larger than the largest screen size,
  // but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
  var BIG_NUM = 1000000000;
  var x = flyoutRect.left;
  var width = flyoutRect.width;

  if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
    return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width,
        BIG_NUM * 2);
  } else {  // Right
    // Firefox sometimes reports the wrong value for the client rect.
    // See https://github.com/google/blockly/issues/1425 and
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1066435
    if (goog.userAgent.GECKO &&
        this.targetWorkspace_ && this.targetWorkspace_.isMutator) {
      // If we're in a mutator, its scale is always 1, purely because of some
      // oddities in our rendering optimizations.  The actual scale is the same
      // as the scale on the parent workspace.
      var scale = this.targetWorkspace_.options.parentWorkspace.scale;
      x += this.leftEdge_ * scale;

    }
    return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, BIG_NUM * 2);
  }
};

/**
 * Compute width of flyout.  Position mat under each block.
 * For RTL: Lay out the blocks and buttons to be right-aligned.
 * @private
 */
Blockly.VerticalFlyout.prototype.reflowInternal_ = function() {
  this.workspace_.scale = this.targetWorkspace_.scale;
  var flyoutWidth = 0;
  var blocks = this.workspace_.getTopBlocks(false);
  for (var i = 0, block; block = blocks[i]; i++) {
    var width = block.getHeightWidth().width;
    if (block.outputConnection) {
      width -= Blockly.BlockSvg.TAB_WIDTH;
    }
    flyoutWidth = Math.max(flyoutWidth, width);
  }
  for (var i = 0, button; button = this.buttons_[i]; i++) {
    flyoutWidth = Math.max(flyoutWidth, button.width);
  }
  flyoutWidth += this.MARGIN * 1.5 + Blockly.BlockSvg.TAB_WIDTH;
  flyoutWidth *= this.workspace_.scale;
  flyoutWidth += Blockly.Scrollbar.scrollbarThickness;

  if (this.width_ != flyoutWidth) {
    for (var i = 0, block; block = blocks[i]; i++) {
      if (this.RTL) {
        // With the flyoutWidth known, right-align the blocks.
        var oldX = block.getRelativeToSurfaceXY().x;
        var newX = flyoutWidth / this.workspace_.scale - this.MARGIN -
            Blockly.BlockSvg.TAB_WIDTH;
        block.moveBy(newX - oldX, 0);
      }
      if (block.flyoutRect_) {
        this.moveRectToBlock_(block.flyoutRect_, block);
      }
    }
    if (this.RTL) {
      // With the flyoutWidth known, right-align the buttons.
      for (var i = 0, button; button = this.buttons_[i]; i++) {
        var y = button.getPosition().y;
        var x = flyoutWidth / this.workspace_.scale - button.width - this.MARGIN -
            Blockly.BlockSvg.TAB_WIDTH;
        button.moveTo(x, y);
      }
    }
    // Record the width for .getMetrics_ and .position.
    this.width_ = flyoutWidth;
    // Call this since it is possible the trash and zoom buttons need
    // to move. e.g. on a bottom positioned flyout when zoom is clicked.
    this.targetWorkspace_.resize();
  }
};
<|endoftext|>


<commit_before>import chai from 'chai';
import spies from 'chai-spies';
import jsdom from 'mocha-jsdom';
import addEventToNodes from './addEventToNodes';

describe('addEventToNodes', () => {

    jsdom();
    chai.use(spies);

    it('should add event to single element', () => {
        const elements = [];
        const evt = () => true;
        const spy = chai.spy(evt);

        // Add elements to array
        elements[0] = document.createElement('button');

        // Attach listener
        addEventToNodes('click', elements, spy);

        // Trigger click
        elements[0].click();

        // Check that function was called
        chai.expect(spy).to.have.been.called();
    });

    it('should add event to multiple elements', () => {
        const elements = [];
        const evt = () => true;
        const spy = chai.spy(evt);

        // Add elements to array
        elements[0] = document.createElement('button');
        elements[1] = document.createElement('button');

        // Attach listeners
        addEventToNodes('click', elements, spy);

        // Trigger clicks
        elements[0].click();
        elements[1].click();

        // Check that function was called twice
        chai.expect(spy).to.have.been.called.twice();
    });

});
<commit_msg>Removed redundant evt in addEventToNodes test<commit_after>import chai from 'chai';
import spies from 'chai-spies';
import jsdom from 'mocha-jsdom';
import addEventToNodes from './addEventToNodes';

describe('addEventToNodes', () => {

    jsdom();
    chai.use(spies);

    it('should add event to single element', () => {
        const elements = [];
        const spy = chai.spy();

        // Add elements to array
        elements[0] = document.createElement('button');

        // Attach listener
        addEventToNodes('click', elements, spy);

        // Trigger click
        elements[0].click();

        // Check that function was called
        chai.expect(spy).to.have.been.called();
    });

    it('should add event to multiple elements', () => {
        const elements = [];
        const spy = chai.spy();

        // Add elements to array
        elements[0] = document.createElement('button');
        elements[1] = document.createElement('button');

        // Attach listeners
        addEventToNodes('click', elements, spy);

        // Trigger clicks
        elements[0].click();
        elements[1].click();

        // Check that function was called twice
        chai.expect(spy).to.have.been.called.twice();
    });

});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  db: 'mongodb://10.0.3.30/mean-test',
  port: 27017,
  app: {
    name: 'MEAN - A Modern Stack - Test'
  },
  facebook: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/facebook/callback'
  },
  twitter: {
    clientID: 'CONSUMER_KEY',
    clientSecret: 'CONSUMER_SECRET',
    callbackURL: 'http://localhost:3000/auth/twitter/callback'
  },
  github: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/github/callback'
  },
  google: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/google/callback'
  },
  linkedin: {
    clientID: 'API_KEY',
    clientSecret: 'SECRET_KEY',
    callbackURL: 'http://localhost:3000/auth/linkedin/callback'
  },
  emailFrom: 'SENDER EMAIL ADDRESS', // sender address like ABC <abc@example.com>
  mailer: {
    service: 'SERVICE_PROVIDER',
    auth: {
      user: 'EMAIL_ID',
      pass: 'PASSWORD'
    }
  }
};
<commit_msg>Tries to use environment variables to connect to db in wercker<commit_after>'use strict';

module.exports = {
  db: 'mongodb://' + process.env.WERCKER_MONGODB_HOST,
  port: process.env.WERCKER_MONGODB_PORT,
  app: {
    name: 'MEAN - A Modern Stack - Test'
  },
  facebook: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/facebook/callback'
  },
  twitter: {
    clientID: 'CONSUMER_KEY',
    clientSecret: 'CONSUMER_SECRET',
    callbackURL: 'http://localhost:3000/auth/twitter/callback'
  },
  github: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/github/callback'
  },
  google: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/google/callback'
  },
  linkedin: {
    clientID: 'API_KEY',
    clientSecret: 'SECRET_KEY',
    callbackURL: 'http://localhost:3000/auth/linkedin/callback'
  },
  emailFrom: 'SENDER EMAIL ADDRESS', // sender address like ABC <abc@example.com>
  mailer: {
    service: 'SERVICE_PROVIDER',
    auth: {
      user: 'EMAIL_ID',
      pass: 'PASSWORD'
    }
  }
};
<|endoftext|>


<commit_before>/** Renders fieldset of a FormSectionGroup object
 * @param document {object} - document object, usually DOM
 * @param options {object} -
 * {
 *  append {object} - domjs object to append
 *  customize {function} - customization callback
 *  formId {string} - the id of the parent form
 *  master {object} - by default section's master object
 *  prepend {object} - domjs object to prepend
 * }
 * @returns {array} - array of constructed domjs elements
 */
'use strict';

var d                = require('d')
  , db               = require('mano').db
  , ns               = require('mano').domjs.ns
  , find             = require('es5-ext/array/#/find')
  , forEach          = require('es5-ext/object/for-each')
  , progressRules    = require('../components/progress-rules')
  , normalizeOptions = require('es5-ext/object/normalize-options');

require('./form-section-base');

module.exports = Object.defineProperties(db.FormSectionGroup.prototype, {
	toDOMFieldset: d(function (document/*, options */) {
		var options         = normalizeOptions(arguments[1])
		  , master          = options.master || this.master
		  , resolvent       = this.getFormResolvent(options)
		  , customizeData   = { master: master }
		  , fieldsetOptions = {
			master: master,
			formId: options.formId,
			disablePartialSubmit: options.disablePartialSubmit != null ? options.disablePartialSubmit
				: this.disablePartialSubmit
		};

		customizeData.arrayResult = [
			options.prepend,
			resolvent.formResolvent,
			progressRules(this, { translationInserts: options.translationInserts })
		];

		customizeData.subSections = customizeData.arrayResult.subSections = {};

		customizeData.arrayResult.push(
			ns.div({ id: resolvent.affectedSectionId }, ns.list(this.sections,
				function (subSection, subSectionName) {
					customizeData.subSections[subSectionName] = {};
					customizeData.subSections[subSectionName].object = subSection;
					return ns.div(
						{ class: 'section-primary-sub', id: subSection.domId },
						ns._if(subSection.label, ns.h3(subSection.label)),
						ns._if(subSection._legend, ns.div({ class: 'section-primary-legend' },
							ns.md(subSection._legend))),
						customizeData.subSections[subSectionName].arrayResult
							= subSection.toDOMFieldset(document, fieldsetOptions)
					);
				}, this), resolvent.legacyScript).extend(options.append)
		);

		if (typeof options.customize === 'function') {
			forEach(customizeData.subSections, function (subSection) {
				subSection.fieldset = find.call(subSection.arrayResult, function (el) {
					return el && el.nodeName === 'FIELDSET';
				})._dbjsFieldset;
			});
			options.customize.call(this, customizeData);
		}

		return customizeData.arrayResult;
	})
});
<commit_msg>Issue #969. Take applicability into account while drawing group subsections.<commit_after>/** Renders fieldset of a FormSectionGroup object
 * @param document {object} - document object, usually DOM
 * @param options {object} -
 * {
 *  append {object} - domjs object to append
 *  customize {function} - customization callback
 *  formId {string} - the id of the parent form
 *  master {object} - by default section's master object
 *  prepend {object} - domjs object to prepend
 * }
 * @returns {array} - array of constructed domjs elements
 */
'use strict';

var d                = require('d')
  , db               = require('mano').db
  , ns               = require('mano').domjs.ns
  , find             = require('es5-ext/array/#/find')
  , forEach          = require('es5-ext/object/for-each')
  , progressRules    = require('../components/progress-rules')
  , normalizeOptions = require('es5-ext/object/normalize-options');

require('./form-section-base');

module.exports = Object.defineProperties(db.FormSectionGroup.prototype, {
	toDOMFieldset: d(function (document/*, options */) {
		var options         = normalizeOptions(arguments[1])
		  , master          = options.master || this.master
		  , resolvent       = this.getFormResolvent(options)
		  , customizeData   = { master: master }
		  , fieldsetOptions = {
			master: master,
			formId: options.formId,
			disablePartialSubmit: options.disablePartialSubmit != null ? options.disablePartialSubmit
				: this.disablePartialSubmit
		};

		customizeData.arrayResult = [
			options.prepend,
			resolvent.formResolvent,
			progressRules(this, { translationInserts: options.translationInserts })
		];

		customizeData.subSections = customizeData.arrayResult.subSections = {};

		customizeData.arrayResult.push(
			ns.div({ id: resolvent.affectedSectionId }, ns.list(this.applicableSections,
				function (subSection, subSectionName) {
					customizeData.subSections[subSectionName] = {};
					customizeData.subSections[subSectionName].object = subSection;
					return ns.div(
						{ class: 'section-primary-sub', id: subSection.domId },
						ns._if(subSection.label, ns.h3(subSection.label)),
						ns._if(subSection._legend, ns.div({ class: 'section-primary-legend' },
							ns.md(subSection._legend))),
						customizeData.subSections[subSectionName].arrayResult
							= subSection.toDOMFieldset(document, fieldsetOptions)
					);
				}, this), resolvent.legacyScript).extend(options.append)
		);

		if (typeof options.customize === 'function') {
			forEach(customizeData.subSections, function (subSection) {
				subSection.fieldset = find.call(subSection.arrayResult, function (el) {
					return el && el.nodeName === 'FIELDSET';
				})._dbjsFieldset;
			});
			options.customize.call(this, customizeData);
		}

		return customizeData.arrayResult;
	})
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {

  var defaultWarnHandler = grunt.fail.warn;

  function warn(){
    var warnings = grunt.config('grunt-continue:warnings') || [];
    warnings.push(arguments);
    grunt.config.set('grunt-continue:warnings', warnings);
    defaultWarnHandler.apply(grunt, Array.prototype.slice.call(arguments));
  }

  grunt.registerTask('continue:on', 'Continue after failing tasks', function() {
    grunt.warn = grunt.fail.warn = warn;
    overridden = grunt.config('grunt-continue:overridden') || false;
    if (!overridden) {
      count = grunt.config('grunt-continue:count') || 0;
      if (!count && grunt.option('force')) {
        grunt.config('grunt-continue:overridden', true);
      } else {
        if (!count) {
          grunt.option('force', true);
        }
        grunt.config.set('grunt-continue:count', ++count);
      }
    }
  });

  grunt.registerTask('continue:off', 'Stop continuing after failing tasks', function() {
    grunt.warn = grunt.fail.warn = defaultWarnHandler;
    overridden = grunt.config('grunt-continue:overridden') || false;
    if (!overridden) {
      count = grunt.config('grunt-continue:count') || 0;
      if (!count) return false;
      grunt.config.set('grunt-continue:count', --count);
      if (!count) {
        grunt.option('force', false);
      }
    }
  });

  grunt.registerTask('continue:fail-on-warning', 'Check to see if there were any warnings, fail if there were', function(){
    var warnings = grunt.config('grunt-continue:warnings') || null;
    if(warnings) {
        var msg = grunt.util.pluralize(warnings.length, 'A warning has/Warnings have')+' occurred between continue:on and continue:off:\n';
        for(var i = 0, len = warnings.length; i<len; i++) {
            msg += ' - '+JSON.stringify(warnings[i])+'\n';
        }
      grunt.warn(grunt.util.normalizelf(msg));
    }
  });
};<commit_msg>adding better messaging output depending if string or object<commit_after>module.exports = function(grunt) {

    var defaultWarnHandler = grunt.fail.warn;

    function warn(){
        var warnings = grunt.config('grunt-continue:warnings') || [];
        warnings.push(arguments[0]);
        grunt.config.set('grunt-continue:warnings', warnings);
        defaultWarnHandler.apply(grunt, Array.prototype.slice.call(arguments));
    }

    grunt.registerTask('continue:on', 'Continue after failing tasks', function() {
        grunt.warn = grunt.fail.warn = warn;
        overridden = grunt.config('grunt-continue:overridden') || false;
        if (!overridden) {
            count = grunt.config('grunt-continue:count') || 0;
            if (!count && grunt.option('force')) {
                grunt.config('grunt-continue:overridden', true);
            } else {
                if (!count) {
                    grunt.option('force', true);
                }
                grunt.config.set('grunt-continue:count', ++count);
            }
        }
    });

    grunt.registerTask('continue:off', 'Stop continuing after failing tasks', function() {
        grunt.warn = grunt.fail.warn = defaultWarnHandler;
        overridden = grunt.config('grunt-continue:overridden') || false;
        if (!overridden) {
            count = grunt.config('grunt-continue:count') || 0;
            if (!count) return false;
            grunt.config.set('grunt-continue:count', --count);
            if (!count) {
                grunt.option('force', false);
            }
        }
    });

    grunt.registerTask('continue:fail-on-warning', 'Check to see if there were any warnings, fail if there were', function(){
        var warnings = grunt.config('grunt-continue:warnings') || null;
        if(warnings) {
            var msg = grunt.util.pluralize(warnings.length, 'A warning has/Warnings have')+' occurred between continue:on and continue:off:\n';
            for(var i = 0, len = warnings.length; i<len; i++) {
                msg += ' - ';
                msg += grunt.util.kindOf(warnings[i]) == 'string'?warnings[i]:JSON.stringify(warnings[i]);
                msg += '\n';
            }
            msg += '\n';
            grunt.warn(grunt.util.normalizelf(msg));
        }
    });
};<|endoftext|>


<commit_before>var express = require('express');
var fs = require('fs');
var app = express();
var port = process.env.PORT || 8080;
var bp = require('body-parser');
var user = require('./middleware/User.js');
var connect = require('connect-ensure-login');

var products = require('./database/access_products.js');
var users = require('./database/access_users.js');
var passport = require('passport');
var codes = require('./middleware/code.js');
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);

// this is for authentication

//var loggedOn = require('connect-ensure-login');
var geoip = require('geoip-lite');

var bp = require('body-parser');
var jobsFilename = './jobs.json';

// these are used in the authentication
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true,maxAge :20000 }));

// app.use(express.session({secret:'andyisawesome',
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));

//use for accesing local files
app.use(express.static('/public'));
app.use('/public', express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/public'));

app.use(bp.urlencoded({extended:true}));
// Add headers
app.use(function (req, res, next) {
		// Website you wish to allow to connect
		res.setHeader('Access-Control-Allow-Origin', '*');
		// Request methods you wish to allow
		res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE, XHR');
		// Request headers you wish to allow
		res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
		// Set to true if you need the website to include cookies in the requests sent
		// to the API (e.g. in case you use sessions)
		res.setHeader('Access-Control-Allow-Credentials', true);
		// Pass to next layer of middleware
		next();
		});

app.set('public', __dirname + '/public');
app.set('view engine', 'ejs');
app.use(bp.json());

// this is the passprt authentication methods
require('./middleware/Config.js')(passport);
var auth = require('./middleware/authentication.js');

// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());
// app.use(passport.session({
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));

//=====================================
//GET METHODS
//=====================================

// app.get('*',function(req,res,next){
//   if(req.headers['x-forwarded-proto']!='https')//&&process.env.NODE_ENV === 'production')
//     res.redirect('https://'+req.hostname+req.url)
//   else
//     next()
// });

app.get('/', function(req,res){
		res.render('index',{'user':req.user});
		});

app.get('/search*', products.search);

app.get('/id/*', products.get_from_id);

			  app.get('/men*', products.get_me_something);

			  app.get('/women*', products.get_me_something);

			  app.get('/kids*', products.get_me_something);

			  app.get('/login', function(req, res){
			  res.render('login',{'user':req.user})
			  });

app.get('/profile', function (req, res) {
    users.get_kart(req, res);
});

app.get('/logout', function(req, res){
    req.logout();
    req.user = undefined;
    console.log(req.user);
    res.render('index',{'user':req.user});
});

app.get('/register', function (req, res) {
    res.render('register',{'user':req.user})
});

app.get('/aboutus', function (req, res) {
    res.render('aboutus',{'user':req.user})
});

app.get('/local', function (req, res) {
    res.render('local',{user:req.user})
});

app.get('/add_to_cart/*', function (req, res) {
    var url = "" + req.url;
    console.log("url: " + url);
    var array = url.split("/");
    console.log("array: " + array);
    var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
    console.log("id: " + id);
    users.add_to_kart(req, res, id);
})

app.get('/getRecommendations',function (req, res) {
	var ipAddr = req.headers["x-forwarded-for"];
	if (ipAddr){
		var list = ipAddr.split(",");
		ipAddr = list[list.length-1];
	} else {
		ipAddr = req.connection.remoteAddress;
	}
	var geo = geoip.lookup(ipAddr);
	if(req.user == undefined){
		res.render('index', {'user':req.user});
		return;
	}
    //var country = geo.city!=undefined && geo.city!='' && geo.city!=null ? geo.city : geo.country;
	var name = req.user.name;
	if(name!==undefined){
		users.get_recommendations(name, geo, function(results){
			//res.send({recommendation: results});
            res.render('display', {results: results, 'user':req.user});
		});
	}
});

//=====================================
//PUT METHODS
//=====================================

app.put('/', function(req,res){

});

app.put('/login', users.put);

//=====================================
//POST METHODS
//=====================================

app.post('/', function(req,res){
if(req.body.item==undefined){
res.statusCode = 400;
} else{
	postData(req.body.item, true);
	res.statusCode = 200;
}
res.end();
});

//=====================================
//DELETE METHODS
//=====================================

app.delete('/', function(req,res){

		});

//=====================================
//AUTHENTICATION METHODS
//=====================================

//check to see if loggedon with fb and then locally
app.all('/auth/*',checkAuth);
//app.all('/auth/*', connect.ensureLoggedIn();

app.post('/auth/testAuth',auth.testAuth);

app.post('/newUser',auth.newUser);

app.post('/login', function(req,res, next){
passport.authenticate('local',{ failureRedirect: '/login'  },function(err,user,info){
console.log("gets into loacl auth");
console.log(user);

if(user!=false){
console.log("user exists");
console.log("username :" + user);
req.session.username = "'" + user + "'";
req.session.save();
		//return res.redirect('/');
		}
		else{
		console.log("Login unsucessful");
//res.send({redirect: '/'});
//res.status(401).send(user);
}

req.logIn(user, function(err) {
if (err) {
req.session.messages = "Error";
console.log('login Error');
return res.status(401).send(user +" :   " +err);

}
req.session.messages = "Login successfully";
var data = { 'name' : user };
req.session.passport.user = data;
console.log(data +" : " +user);

console.log('login successful');
res.render('index',{'user':req.user});
});
})(req,res,next);
});

// TODO have a database of vaild tokens
app.post('/auth/logout',auth.logout);

app.get('/login/facebook',
passport.authenticate('facebook'));

app.get('/login/facebook/return',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
//console.log(req.data);

var data = { 'name' : req.user.displayName };
req.session.passport.user = data;
//console.log(req.user.displayName);
//var data = {'data':req.user.accessToken};
//'res.render('index', {data:data});

res.render('index', {'user':data});
//console.log(req.user.accessToken);
});

/*app.get('/profile',
//  connect.ensureLoggedIn(),
function(req, res){
  res.render('profile   ', { user: req.user });

});*/

app.get( '/auth/facebook/logout',function( request, response ) {
		request.logout();
		response.send( 'Logged out!' );
		//res.redirect('/');
		});

function checkAuth(req, res, next) {
	if (req.isAuthenticated()){
		return next();
	}
	else{
		res.status(401).send("Failed to authenticate: please login")
	}

}

app.listen(port, function(){
		console.log('Listening:' + port);
		});

app.get('*', function(req, res){
		res.status(400).send("Sorry, that page doesn't exist.");
		});

<commit_msg>handled the case where there is noone logged in for a profile request<commit_after>var express = require('express');
var fs = require('fs');
var app = express();
var port = process.env.PORT || 8080;
var bp = require('body-parser');
var user = require('./middleware/User.js');
var connect = require('connect-ensure-login');

var products = require('./database/access_products.js');
var users = require('./database/access_users.js');
var passport = require('passport');
var codes = require('./middleware/code.js');
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);

// this is for authentication

//var loggedOn = require('connect-ensure-login');
var geoip = require('geoip-lite');

var bp = require('body-parser');
var jobsFilename = './jobs.json';

// these are used in the authentication
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true,maxAge :20000 }));

// app.use(express.session({secret:'andyisawesome',
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));

//use for accesing local files
app.use(express.static('/public'));
app.use('/public', express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/public'));

app.use(bp.urlencoded({extended:true}));
// Add headers
app.use(function (req, res, next) {
		// Website you wish to allow to connect
		res.setHeader('Access-Control-Allow-Origin', '*');
		// Request methods you wish to allow
		res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE, XHR');
		// Request headers you wish to allow
		res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
		// Set to true if you need the website to include cookies in the requests sent
		// to the API (e.g. in case you use sessions)
		res.setHeader('Access-Control-Allow-Credentials', true);
		// Pass to next layer of middleware
		next();
		});

app.set('public', __dirname + '/public');
app.set('view engine', 'ejs');
app.use(bp.json());

// this is the passprt authentication methods
require('./middleware/Config.js')(passport);
var auth = require('./middleware/authentication.js');

// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());
// app.use(passport.session({
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));

//=====================================
//GET METHODS
//=====================================

// app.get('*',function(req,res,next){
//   if(req.headers['x-forwarded-proto']!='https')//&&process.env.NODE_ENV === 'production')
//     res.redirect('https://'+req.hostname+req.url)
//   else
//     next()
// });

app.get('/', function(req,res){
		res.render('index',{'user':req.user});
		});

app.get('/search*', products.search);

app.get('/id/*', products.get_from_id);

			  app.get('/men*', products.get_me_something);

			  app.get('/women*', products.get_me_something);

			  app.get('/kids*', products.get_me_something);

			  app.get('/login', function(req, res){
			  res.render('login',{'user':req.user})
			  });

app.get('/profile', function (req, res) {
	if(req.user.name == undefined){
		res.render('index', {'user': req.user});
	}
    users.get_kart(req, res);
});

app.get('/logout', function(req, res){
    req.logout();
    req.user = undefined;
    console.log(req.user);
    res.render('index',{'user':req.user});
});

app.get('/register', function (req, res) {
    res.render('register',{'user':req.user})
});

app.get('/aboutus', function (req, res) {
    res.render('aboutus',{'user':req.user})
});

app.get('/local', function (req, res) {
    res.render('local',{user:req.user})
});

app.get('/add_to_cart/*', function (req, res) {
    var url = "" + req.url;
    console.log("url: " + url);
    var array = url.split("/");
    console.log("array: " + array);
    var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
    console.log("id: " + id);
    users.add_to_kart(req, res, id);
})

app.get('/getRecommendations',function (req, res) {
	var ipAddr = req.headers["x-forwarded-for"];
	if (ipAddr){
		var list = ipAddr.split(",");
		ipAddr = list[list.length-1];
	} else {
		ipAddr = req.connection.remoteAddress;
	}
	var geo = geoip.lookup(ipAddr);
	if(req.user == undefined){
		res.render('index', {'user':req.user});
		return;
	}
    //var country = geo.city!=undefined && geo.city!='' && geo.city!=null ? geo.city : geo.country;
	var name = req.user.name;
	if(name!==undefined){
		users.get_recommendations(name, geo, function(results){
			//res.send({recommendation: results});
            res.render('display', {results: results, 'user':req.user});
		});
	}
});

//=====================================
//PUT METHODS
//=====================================

app.put('/', function(req,res){

});

app.put('/login', users.put);

//=====================================
//POST METHODS
//=====================================

app.post('/', function(req,res){
if(req.body.item==undefined){
res.statusCode = 400;
} else{
	postData(req.body.item, true);
	res.statusCode = 200;
}
res.end();
});

//=====================================
//DELETE METHODS
//=====================================

app.delete('/', function(req,res){

		});

//=====================================
//AUTHENTICATION METHODS
//=====================================

//check to see if loggedon with fb and then locally
app.all('/auth/*',checkAuth);
//app.all('/auth/*', connect.ensureLoggedIn();

app.post('/auth/testAuth',auth.testAuth);

app.post('/newUser',auth.newUser);

app.post('/login', function(req,res, next){
passport.authenticate('local',{ failureRedirect: '/login'  },function(err,user,info){
console.log("gets into loacl auth");
console.log(user);

if(user!=false){
console.log("user exists");
console.log("username :" + user);
req.session.username = "'" + user + "'";
req.session.save();
		//return res.redirect('/');
		}
		else{
		console.log("Login unsucessful");
//res.send({redirect: '/'});
//res.status(401).send(user);
}

req.logIn(user, function(err) {
if (err) {
req.session.messages = "Error";
console.log('login Error');
return res.status(401).send(user +" :   " +err);

}
req.session.messages = "Login successfully";
var data = { 'name' : user };
req.session.passport.user = data;
console.log(data +" : " +user);

console.log('login successful');
res.render('index',{'user':req.user});
});
})(req,res,next);
});

// TODO have a database of vaild tokens
app.post('/auth/logout',auth.logout);

app.get('/login/facebook',
passport.authenticate('facebook'));

app.get('/login/facebook/return',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
//console.log(req.data);

var data = { 'name' : req.user.displayName };
req.session.passport.user = data;
//console.log(req.user.displayName);
//var data = {'data':req.user.accessToken};
//'res.render('index', {data:data});

res.render('index', {'user':data});
//console.log(req.user.accessToken);
});

/*app.get('/profile',
//  connect.ensureLoggedIn(),
function(req, res){
  res.render('profile   ', { user: req.user });

});*/

app.get( '/auth/facebook/logout',function( request, response ) {
		request.logout();
		response.send( 'Logged out!' );
		//res.redirect('/');
		});

function checkAuth(req, res, next) {
	if (req.isAuthenticated()){
		return next();
	}
	else{
		res.status(401).send("Failed to authenticate: please login")
	}

}

app.listen(port, function(){
		console.log('Listening:' + port);
		});

app.get('*', function(req, res){
		res.status(400).send("Sorry, that page doesn't exist.");
		});

<|endoftext|>


<commit_before>// Copyright 2013-2020, University of Colorado Boulder

/**
 * Outputs a number for use in SVG's style/transform/path strings.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

import dot from './dot.js';

/**
 * Outputs a number for use in SVG's style/transform/path strings.
 *
 * We need to prevent the numbers from being in an exponential toString form, since the CSS transform does not support
 * that.
 *
 * @param {number} number
 * @returns {string}
 */
function toSVGNumber( number ) {
  // Largest guaranteed number of digits according to https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed
  // See https://github.com/phetsims/dot/issues/36
  return number.toFixed( 20 );
}

dot.register( 'toSVGNumber', toSVGNumber );
export default toSVGNumber;<commit_msg>update copyright dates from daily grunt work<commit_after>// Copyright 2020, University of Colorado Boulder

/**
 * Outputs a number for use in SVG's style/transform/path strings.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

import dot from './dot.js';

/**
 * Outputs a number for use in SVG's style/transform/path strings.
 *
 * We need to prevent the numbers from being in an exponential toString form, since the CSS transform does not support
 * that.
 *
 * @param {number} number
 * @returns {string}
 */
function toSVGNumber( number ) {
  // Largest guaranteed number of digits according to https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed
  // See https://github.com/phetsims/dot/issues/36
  return number.toFixed( 20 );
}

dot.register( 'toSVGNumber', toSVGNumber );
export default toSVGNumber;<|endoftext|>


<commit_before>/*
 * ext-arrows.js
 *
 * Licensed under the Apache License, Version 2
 *
 * Copyright(c) 2010 Alexis Deveria
 *
 */

$(function() {
	svgCanvas.addExtension("Arrows", function(S) {
		var svgcontent = S.svgcontent,
			addElem = S.addSvgElementFromJson,
			selElems;

		var lang_list = {
			"en":[
				{"id": "arrow_none", "textContent": "No arrow" }
			],
			"fr":[
				{"id": "arrow_none", "textContent": "Sans flèche" }
			]
		};

		function showPanel(on) {
			$('#arrow_panel').toggle(on);

			if(on) {
				var el = selElems[0];
				var end = el.getAttribute("marker-end");
				var start = el.getAttribute("marker-start");
				var mid = el.getAttribute("marker-mid");
				var val;

				if(end && start) {
					val = "both";
				} else if(end) {
					val = "end";
				} else if(start) {
					val = "start";
				} else if(mid) {
					val = "mid";
					if(mid.indexOf("bk") != -1) {
						val = "mid_bk";
					}
				}

				if(!start && !mid && !end) {
					val = "none";
				}

				$("#arrow_list").val(val);
			}
		}

		function resetMarker() {
			var el = selElems[0];
			el.removeAttribute("marker-start");
			el.removeAttribute("marker-mid");
			el.removeAttribute("marker-end");
		}

		function addMarker(id, type) {
			// TODO: Make marker (or use?) per arrow type, since refX can be different
			var marker = S.getElem(id);

			var pathdata = {
				se_arrow_fw: {d:"m0,0l10,5l-10,5l5,-5l-5,-5z", refx:8},
				se_arrow_bk: {d:"m10,0l-10,5l10,5l-5,-5l5,-5z", refx:2}
			}

			var data = pathdata[id];

			if(type == "mid") {
				data.refx = 5;
			}

			if(!marker) {
				marker = addElem({
					"element": "marker",
					"attr": {
						"viewBox": "0 0 10 10",
						"id": id,
						"refY": 5,
						"markerUnits": "strokeWidth",
						"markerWidth": 5,
						"markerHeight": 5,
						"orient": "auto"
					}
				});
				var arrow = addElem({
					"element": "path",
					"attr": {
						"d": data.d,
						"fill": "#000"
					}
				});
				marker.appendChild(arrow);
				S.findDefs().appendChild(marker);
			}

			marker.setAttribute('refX', data.refx);
		}

		function setArrow() {
			var type = this.value;
			resetMarker();

			if(type == "none") {
				return;
			}

			var fw_id = "se_arrow_fw";
			var bk_id = "se_arrow_bk";

			// Set marker on element
			var id = fw_id;
			if(type == "mid_bk") {
				type = "mid";
				id = bk_id;
			} else if(type == "both") {
				addMarker(bk_id, type);
				svgCanvas.changeSelectedAttribute("marker-start", "url(#" + bk_id + ")");
				type = "end";
				id = fw_id;
			} else if (type == "start") {
				id = bk_id;
			}

			addMarker(id, type);
			svgCanvas.changeSelectedAttribute("marker-"+type, "url(#" + id + ")");
			S.call("changed", selElems);
		}

		function colorChanged(elem) {
			var color = elem.getAttribute('stroke');

			var markers = ['start','mid','end'];

			$.each(markers, function(i, type) {
				var href = elem.getAttribute('marker-'+type);
				if(href) {
					var marker_id = href.match(/\(\#(.*)\)/)[1];
					var marker = S.getElem(marker_id);
					var shape = marker.firstChild;
					var cur_color = shape.getAttribute('fill');
					console.log(cur_color, color);

					// If color matches, ignore
					// If color doesn't match, look for marker with shape that does match color
					// - Found? Use its URL!
					// - Not found? Create new one (based on this one) but with new fill
					// (don't remove old marker: removeUnused* can take care of that)
				}

			});

		}

		return {
			name: "Arrows",
			context_tools: [{
				type: "select",
				panel: "arrow_panel",
				title: "Select arrow type",
				id: "arrow_list",
				options: {
					none: "No arrow",
					end: "---->",
					start: "<----",
					both: "<--->",
					mid: "-->--",
					mid_bk: "--<--"
				},
				defval: "none",
				events: {
					change: setArrow
				}
			}],
			callback: function() {
				$('#arrow_panel').hide();
			},
			addLangData: function(lang) {
				return {
					data: lang_list[lang]
				};
			},
			selectedChanged: function(opts) {

				// Use this to update the current selected elements
				selElems = opts.elems;

				var i = selElems.length;
				var marker_elems = ['line','path','polyline','polygon'];

				while(i--) {
					var elem = selElems[i];
					if(elem && $.inArray(elem.tagName, marker_elems) != -1) {
						if(opts.selectedElement && !opts.multiselected) {
							showPanel(true);
						} else {
							showPanel(false);
						}
					} else {
						showPanel(false);
					}
				}
			},
			elementChanged: function(opts) {
				var elem = opts.elems[0];
				if(elem && (
					elem.getAttribute("marker-start") ||
					elem.getAttribute("marker-mid") ||
					elem.getAttribute("marker-end")
				)) {
	// 								var start = elem.getAttribute("marker-start");
	// 								var mid = elem.getAttribute("marker-mid");
	// 								var end = elem.getAttribute("marker-end");
					// Has marker, so see if it should match color
					colorChanged(elem);
				}

			}
		};
	});
});
<commit_msg>Made arrows match stroke color<commit_after>/*
 * ext-arrows.js
 *
 * Licensed under the Apache License, Version 2
 *
 * Copyright(c) 2010 Alexis Deveria
 *
 */

$(function() {
	svgCanvas.addExtension("Arrows", function(S) {
		var svgcontent = S.svgcontent,
			addElem = S.addSvgElementFromJson,
			selElems;

		var lang_list = {
			"en":[
				{"id": "arrow_none", "textContent": "No arrow" }
			],
			"fr":[
				{"id": "arrow_none", "textContent": "Sans flèche" }
			]
		};

		var pathdata = {
			fw: {d:"m0,0l10,5l-10,5l5,-5l-5,-5z", refx:8, id:"se_arrow_fw"},
			bk: {d:"m10,0l-10,5l10,5l-5,-5l5,-5z", refx:2, id:"se_arrow_bk"}
		}
		function getLinked(elem, attr) {
			var str = elem.getAttribute(attr);
			if(!str) return null;
			var m = str.match(/\(\#(.*)\)/);
			if(!m || m.length !== 2) {
				return null;
			}
			return S.getElem(m[1]);
		}

		function showPanel(on) {
			$('#arrow_panel').toggle(on);

			if(on) {
				var el = selElems[0];
				var end = el.getAttribute("marker-end");
				var start = el.getAttribute("marker-start");
				var mid = el.getAttribute("marker-mid");
				var val;

				if(end && start) {
					val = "both";
				} else if(end) {
					val = "end";
				} else if(start) {
					val = "start";
				} else if(mid) {
					val = "mid";
					if(mid.indexOf("bk") != -1) {
						val = "mid_bk";
					}
				}

				if(!start && !mid && !end) {
					val = "none";
				}

				$("#arrow_list").val(val);
			}
		}

		function resetMarker() {
			var el = selElems[0];
			el.removeAttribute("marker-start");
			el.removeAttribute("marker-mid");
			el.removeAttribute("marker-end");
		}

		function addMarker(dir, type, id) {
			// TODO: Make marker (or use?) per arrow type, since refX can be different
			id = id || 'se_arrow_' + dir;

			var marker = S.getElem(id);

			var data = pathdata[dir];

			if(type == "mid") {
				data.refx = 5;
			}

			if(!marker) {
				marker = addElem({
					"element": "marker",
					"attr": {
						"viewBox": "0 0 10 10",
						"id": id,
						"refY": 5,
						"markerUnits": "strokeWidth",
						"markerWidth": 5,
						"markerHeight": 5,
						"orient": "auto",
						"style": "pointer-events:none" // Currently needed for Opera
					}
				});
				var arrow = addElem({
					"element": "path",
					"attr": {
						"d": data.d,
						"fill": "#000000"
					}
				});
				marker.appendChild(arrow);
				S.findDefs().appendChild(marker);
			}

			marker.setAttribute('refX', data.refx);

			return marker;
		}

		function setArrow() {
			var type = this.value;
			resetMarker();

			if(type == "none") {
				return;
			}

			// Set marker on element
			var dir = "fw";
			if(type == "mid_bk") {
				type = "mid";
				dir = "bk";
			} else if(type == "both") {
				addMarker("bk", type);
				svgCanvas.changeSelectedAttribute("marker-start", "url(#" + pathdata.bk.id + ")");
				type = "end";
				dir = "fw";
			} else if (type == "start") {
				dir = "bk";
			}

			addMarker(dir, type);
			svgCanvas.changeSelectedAttribute("marker-"+type, "url(#" + pathdata[dir].id + ")");
			S.call("changed", selElems);
		}

		function colorChanged(elem) {
			var color = elem.getAttribute('stroke');

			var mtypes = ['start','mid','end'];
			var defs = S.findDefs();

			$.each(mtypes, function(i, type) {
				var marker = getLinked(elem, 'marker-'+type);
				if(!marker) return;

				var cur_color = $(marker).children().attr('fill');
				var cur_d = $(marker).children().attr('d');
				var new_marker = null;
				if(cur_color === color) return;

				var all_markers = $(defs).find('marker');
				// Different color, check if already made
				all_markers.each(function() {
					var attrs = $(this).children().attr(['fill', 'd']);
					if(attrs.fill === color && attrs.d === cur_d) {
						// Found another marker with this color and this path
						new_marker = this;
					}
				});

				if(!new_marker) {
					// Create a new marker with this color
					var last_id = marker.id;
					var dir = last_id.indexOf('_fw') !== -1?'fw':'bk';

					new_marker = addMarker(dir, type, 'se_arrow_' + dir + all_markers.length);

					$(new_marker).children().attr('fill', color);
				}

				$(elem).attr('marker-'+type, "url(#" + new_marker.id + ")");

				// Check if last marker can be removed
				var remove = true;
				$(S.svgcontent).find('line, polyline, path, polygon').each(function() {
					var elem = this;
					$.each(mtypes, function(j, mtype) {
						if($(elem).attr('marker-' + mtype) === "url(#" + marker.id + ")") {
							return remove = false;
						}
					});
					if(!remove) return false;
				});

				// Not found, so can safely remove
				if(remove) {
					$(marker).remove();
				}

			});

		}

		return {
			name: "Arrows",
			context_tools: [{
				type: "select",
				panel: "arrow_panel",
				title: "Select arrow type",
				id: "arrow_list",
				options: {
					none: "No arrow",
					end: "---->",
					start: "<----",
					both: "<--->",
					mid: "-->--",
					mid_bk: "--<--"
				},
				defval: "none",
				events: {
					change: setArrow
				}
			}],
			callback: function() {
				$('#arrow_panel').hide();
			},
			addLangData: function(lang) {
				return {
					data: lang_list[lang]
				};
			},
			selectedChanged: function(opts) {

				// Use this to update the current selected elements
				selElems = opts.elems;

				var i = selElems.length;
				var marker_elems = ['line','path','polyline','polygon'];

				while(i--) {
					var elem = selElems[i];
					if(elem && $.inArray(elem.tagName, marker_elems) != -1) {
						if(opts.selectedElement && !opts.multiselected) {
							showPanel(true);
						} else {
							showPanel(false);
						}
					} else {
						showPanel(false);
					}
				}
			},
			elementChanged: function(opts) {
				var elem = opts.elems[0];
				if(elem && (
					elem.getAttribute("marker-start") ||
					elem.getAttribute("marker-mid") ||
					elem.getAttribute("marker-end")
				)) {
	// 								var start = elem.getAttribute("marker-start");
	// 								var mid = elem.getAttribute("marker-mid");
	// 								var end = elem.getAttribute("marker-end");
					// Has marker, so see if it should match color
					colorChanged(elem);
				}

			}
		};
	});
});
<|endoftext|>


<commit_before>import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';

import '@polymer/iron-icons/iron-icons.js';
import '@polymer/iron-input/iron-input.js';

import '@polymer/paper-styles/paper-styles.js';

import '@hunsalz/web-socket/web-socket.js';

class TrackedRacerApp extends PolymerElement {
  static get template() {
    return html`

      <web-socket id="ws" auto url="[[url]]" state="{{state}}" verbose></web-socket>

HALLO

[[__getPercent(motorA)]]%

          <input type="range" value="{{motorA::input}}" min="-[[range]]" max="[[range]]" step="[[step]]"></input>

            <iron-icon icon="icons:settings-remote"></iron-icon>

          <input type="range" value="{{motorB::input}}" min="-[[range]]" max="[[range]]" step="[[step]]"></input>

[[__getPercent(motorB)]]%

    `;
  }
  static get properties() {
    return {
      url: {
        type: String,
        notify: true,
        computed: '__computeWSUrl()'
      },
      state: {
        type: Number,
        observer: '__handleWSStateChanges',
        notify: true
      },
      range: {
        type: Number,
        value: 25
      },
      step: {
        type: Number,
        value: 5
      },
      motorA: {
        type: Number,
        value: 0,
        notify: true,
        observer: '__changed'
      },
      motorB: {
        type: Number,
        value: 0,
        notify: true,
        observer: '__changed'
      }
    };
  }

  __changed() {
    this.$.ws.send({ motorA: this.motorA, motorB: this.motorB, mode: 'absolute' }, true);
  }

  __handleWSStateChanges() {

    var _color = 'var(--paper-orange-700)';
    switch (this.state) {
      case 1:
        _color = 'var(--paper-green-700)';
        break;
      case 3:
        _color = 'var(--paper-red-700)';
        break;
      default:
        _color = 'var(--paper-orange-700)';
    }
    this.updateStyles({
      '--state-color': _color
    });
  }

  __getPercent(value) {
    return 100 / this.range * value;
  }

  __computeWSUrl() {

    var wsUrlDev = "wss://echo.websocket.org/";

    switch(window.location.hostname) {
      case "127.0.0.1":
        console.warn(window.location.hostname + " is defined as dev location. Use WebSocket server: " + wsUrlDev + " for dev mode.");
        return wsUrlDev;
      case "github.io":
        console.warn(window.location.hostname + " is defined as dev location. Use WebSocket server: " + wsUrlDev + " for dev mode.");
        return wsUrlDev;
      default:
        return "ws://" + window.location.hostname + ":8000/racer"; // URL must match with device preferences
    }
  }
}

window.customElements.define('tracked-racer-app', TrackedRacerApp);<commit_msg>change WS compute ruleset<commit_after>import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';

import '@polymer/iron-icons/iron-icons.js';
import '@polymer/iron-input/iron-input.js';

import '@polymer/paper-styles/paper-styles.js';

import '@hunsalz/web-socket/web-socket.js';

class TrackedRacerApp extends PolymerElement {
  static get template() {
    return html`

      <web-socket id="ws" auto url="[[url]]" state="{{state}}" verbose></web-socket>

HALLO

[[__getPercent(motorA)]]%

          <input type="range" value="{{motorA::input}}" min="-[[range]]" max="[[range]]" step="[[step]]"></input>

            <iron-icon icon="icons:settings-remote"></iron-icon>

          <input type="range" value="{{motorB::input}}" min="-[[range]]" max="[[range]]" step="[[step]]"></input>

[[__getPercent(motorB)]]%

    `;
  }
  static get properties() {
    return {
      url: {
        type: String,
        notify: true,
        computed: '__computeWSUrl()'
      },
      state: {
        type: Number,
        observer: '__handleWSStateChanges',
        notify: true
      },
      range: {
        type: Number,
        value: 25
      },
      step: {
        type: Number,
        value: 5
      },
      motorA: {
        type: Number,
        value: 0,
        notify: true,
        observer: '__changed'
      },
      motorB: {
        type: Number,
        value: 0,
        notify: true,
        observer: '__changed'
      }
    };
  }

  __changed() {
    this.$.ws.send({ motorA: this.motorA, motorB: this.motorB, mode: 'absolute' }, true);
  }

  __handleWSStateChanges() {

    var _color = 'var(--paper-orange-700)';
    switch (this.state) {
      case 1:
        _color = 'var(--paper-green-700)';
        break;
      case 3:
        _color = 'var(--paper-red-700)';
        break;
      default:
        _color = 'var(--paper-orange-700)';
    }
    this.updateStyles({
      '--state-color': _color
    });
  }

  __getPercent(value) {
    return 100 / this.range * value;
  }

  __computeWSUrl() {

    let hostname = window.location.hostname;
    if (hostname === "127.0.0.1" || hostname.endsWith("github.io")) {
      let wsUrlDev = "wss://echo.websocket.org/";
      console.warn(hostname + " is defined as dev environment. Use " + wsUrlDev + " as mock service.");
      return wsUrlDev;
    } else {
      return "ws://" + window.location.hostname + ":8000/racer"; // URL must match with device preferences
    }
  }
}

window.customElements.define('tracked-racer-app', TrackedRacerApp);<|endoftext|>


<commit_before>$(function() {
	highlightMenu();
});
var menu;
function highlightMenu() {
	if(PHP.page.hasSubMenu == "false"){
		jQuery("#Menu_" + PHP.page.menuEntry)
			.animate({
				backgroundColor: "#ffffff",
				opacity: 0.5
				}, 1500)
			.animate({
				borderRadius: 15,
				borderWidth: 3
				});
	}
}<commit_msg>highlight menu<commit_after>$(function() {
	highlightMenu();
});
var menu;
function highlightMenu() {
	jQuery("#Menu_" + PHP.page.menuEntry)
		.animate({
			backgroundColor: "#ffffff",
			opacity: 0.5
			}, 1500)
		.animate({
			borderRadius: 15,
			borderWidth: 3
			});
}<|endoftext|>


<commit_before>import defineClasses from '../util/defineClasses'

export default function() {
  return defineClasses({
    flex: {
      display: 'flex',
    },
    'inline-flex': {
      display: 'inline-flex',
    },
    'flex-row': {
      'flex-direction': 'row',
    },
    'flex-row-reverse': {
      'flex-direction': 'row-reverse',
    },
    'flex-col': {
      'flex-direction': 'column',
    },
    'flex-col-reverse': {
      'flex-direction': 'column-reverse',
    },
    'flex-wrap': {
      'flex-wrap': 'wrap',
    },
    'flex-wrap-reverse': {
      'flex-wrap': 'wrap-reverse',
    },
    'flex-no-wrap': {
      'flex-wrap': 'nowrap',
    },
    'items-start': {
      'align-items': 'flex-start',
    },
    'items-end': {
      'align-items': 'flex-end',
    },
    'items-center': {
      'align-items': 'center',
    },
    'items-baseline': {
      'align-items': 'baseline',
    },
    'items-stretch': {
      'align-items': 'stretch',
    },
    'self-auto': {
      'align-self': 'auto',
    },
    'self-start': {
      'align-self': 'flex-start',
    },
    'self-end': {
      'align-self': 'flex-end',
    },
    'self-center': {
      'align-self': 'center',
    },
    'self-stretch': {
      'align-self': 'stretch',
    },
    'justify-start': {
      'justify-content': 'flex-start',
    },
    'justify-end': {
      'justify-content': 'flex-end',
    },
    'justify-center': {
      'justify-content': 'center',
    },
    'justify-between': {
      'justify-content': 'space-between',
    },
    'justify-around': {
      'justify-content': 'space-around',
    },
    'content-center': {
      'align-content': 'center',
    },
    'content-start': {
      'align-content': 'flex-start',
    },
    'content-end': {
      'align-content': 'flex-end',
    },
    'content-between': {
      'align-content': 'space-between',
    },
    'content-around': {
      'align-content': 'space-around',
    },
    'flex-1': {
      flex: '1',
    },
    'flex-auto': {
      flex: 'auto',
    },
    'flex-initial': {
      flex: 'initial',
    },
    'flex-none': {
      flex: 'none',
    },
    'flex-grow': {
      'flex-grow': '1',
    },
    'flex-shrink': {
      'flex-shrink': '1',
    },
    'flex-no-grow': {
      'flex-grow': '0',
    },
    'flex-no-shrink': {
      'flex-shrink': '0',
    },
  })
}
<commit_msg>Improve IE support for flex-1 utility<commit_after>import defineClasses from '../util/defineClasses'

export default function() {
  return defineClasses({
    flex: {
      display: 'flex',
    },
    'inline-flex': {
      display: 'inline-flex',
    },
    'flex-row': {
      'flex-direction': 'row',
    },
    'flex-row-reverse': {
      'flex-direction': 'row-reverse',
    },
    'flex-col': {
      'flex-direction': 'column',
    },
    'flex-col-reverse': {
      'flex-direction': 'column-reverse',
    },
    'flex-wrap': {
      'flex-wrap': 'wrap',
    },
    'flex-wrap-reverse': {
      'flex-wrap': 'wrap-reverse',
    },
    'flex-no-wrap': {
      'flex-wrap': 'nowrap',
    },
    'items-start': {
      'align-items': 'flex-start',
    },
    'items-end': {
      'align-items': 'flex-end',
    },
    'items-center': {
      'align-items': 'center',
    },
    'items-baseline': {
      'align-items': 'baseline',
    },
    'items-stretch': {
      'align-items': 'stretch',
    },
    'self-auto': {
      'align-self': 'auto',
    },
    'self-start': {
      'align-self': 'flex-start',
    },
    'self-end': {
      'align-self': 'flex-end',
    },
    'self-center': {
      'align-self': 'center',
    },
    'self-stretch': {
      'align-self': 'stretch',
    },
    'justify-start': {
      'justify-content': 'flex-start',
    },
    'justify-end': {
      'justify-content': 'flex-end',
    },
    'justify-center': {
      'justify-content': 'center',
    },
    'justify-between': {
      'justify-content': 'space-between',
    },
    'justify-around': {
      'justify-content': 'space-around',
    },
    'content-center': {
      'align-content': 'center',
    },
    'content-start': {
      'align-content': 'flex-start',
    },
    'content-end': {
      'align-content': 'flex-end',
    },
    'content-between': {
      'align-content': 'space-between',
    },
    'content-around': {
      'align-content': 'space-around',
    },
    'flex-1': {
      flex: '1 1 0%',
    },
    'flex-auto': {
      flex: 'auto',
    },
    'flex-initial': {
      flex: 'initial',
    },
    'flex-none': {
      flex: 'none',
    },
    'flex-grow': {
      'flex-grow': '1',
    },
    'flex-shrink': {
      'flex-shrink': '1',
    },
    'flex-no-grow': {
      'flex-grow': '0',
    },
    'flex-no-shrink': {
      'flex-shrink': '0',
    },
  })
}
<|endoftext|>


<commit_before>const adviserResult = require('../../../sandbox/fixtures/autocomplete-adviser-list.json')

/**
 * Enter `input` into an advisers typeahead `element` and select the first result
 *
 * This waits for the adviser api request to complete before selecting the
 * first option, but has a mocked intercepted adviser response to circumvent
 * network latency.
 */
export const selectFirstAdvisersTypeaheadOption = ({
  element,
  input,
  mockAdviserResponse = true,
}) =>
  cy.get(element).within(() => {
    cy.intercept(
      `/api-proxy/adviser/?*${input.replace(' ', '+')}*`,
      mockAdviserResponse
        ? {
            body: adviserResult,
          }
        : undefined
    ).as('adviserResults')
    cy.get('input').clear().type(input)
    cy.wait('@adviserResults')
    cy.get('input').type('{downarrow}{enter}{esc}')
  })

/**
 * Clicks the checkbox option with the given value
 */
export const clickCheckboxGroupOption = ({ element, value }) => {
  cy.get(element).find(`input[value="${value}"]`).parent().click()
}

/**
 * Clicks the radiogroup option with the label
 */
export const clickRadioGroupOption = ({ element, label }) => {
  cy.get(element).contains('span', label).parent().click()
}

/**
 * Enter `input` into a typeahead `element` and select the first result
 */
export const selectFirstTypeaheadOption = ({ element, input }) => {
  cy.get(element).type(input)
  cy.get(element).find('[data-test="typeahead-menu-option"]').first().click()
  cy.get(element).find('input').blur()
}

/**
 * Removes a specific chip based on the data-value
 */
export const removeChip = (dataValue) => {
  cy.get('[data-test=filter-chips]').find(`[data-value="${dataValue}"]`).click()
}

/**
 * Adds a date to the any given date input field
 */
export const inputDateValue = ({ element, value }) => {
  cy.get(element).type(value)
}

/**
 * Adds a date object of {day, month, year} to the relevent input fields
 */
export const clearAndInputDateValueObject = ({ element, value }) => {
  cy.wrap(element).then(($el) => {
    cy.wrap($el).find('[data-test$="-day"]').clear().type(value.day.toString())
    cy.wrap($el)
      .find('[data-test$="-month"]')
      .clear()
      .type(value.month.toString())
    cy.wrap($el)
      .find('[data-test$="-year"]')
      .clear()
      .type(value.year.toString())
  })
}

/**
 * Clears and Types a value in an input field
 */
export const clearAndTypeInput = ({ element, value }) => {
  cy.wrap(element).then(($el) => {
    cy.wrap($el)
      .find('input')
      .clear()
      .type(value ?? '')
  })
}

/**
 * Clears and Types a value in an textarea field
 */
export const clearAndTypeTextArea = ({ element, value }) => {
  cy.wrap(element).then(($el) => {
    cy.wrap($el)
      .find('textarea')
      .clear()
      .type(value ?? '')
  })
}

/**
 * Remove the first item in a multi-select Typeahead
 */
export const removeFirstTypeaheadItem = () => {
  cy.get('[data-test="typeahead-chip"]').eq(0).click()
}

/**
 * Click on a button
 * @param {*} buttonText The text of the button you want to click
 */
export const clickButton = (buttonText) => {
  cy.contains('button', buttonText).click()
}

/**
 * Click on a Cancel anchor
 */
export const clickCancelLink = () => {
  cy.get('[data-test="cancel-button"]').click()
}
<commit_msg>TET-227: wait for typeahead to contain result<commit_after>const adviserResult = require('../../../sandbox/fixtures/autocomplete-adviser-list.json')

/**
 * Enter `input` into an advisers typeahead `element` and select the first result
 *
 * This waits for the adviser api request to complete before selecting the
 * first option, but has a mocked intercepted adviser response to circumvent
 * network latency.
 */
export const selectFirstAdvisersTypeaheadOption = ({
  element,
  input,
  mockAdviserResponse = true,
}) =>
  cy.get(element).within(() => {
    cy.intercept(
      `/api-proxy/adviser/?*${input.replace(' ', '+')}*`,
      mockAdviserResponse
        ? {
            body: adviserResult,
          }
        : undefined
    ).as('adviserResults')
    cy.get('input').clear().type(input)
    cy.wait('@adviserResults')
    cy.get('[data-test="typeahead-menu-option"]').contains(input, {
      matchCase: false,
      timeout: 120000,
    })
    cy.get('input').type('{downarrow}{enter}{esc}')
  })

/**
 * Clicks the checkbox option with the given value
 */
export const clickCheckboxGroupOption = ({ element, value }) => {
  cy.get(element).find(`input[value="${value}"]`).parent().click()
}

/**
 * Clicks the radiogroup option with the label
 */
export const clickRadioGroupOption = ({ element, label }) => {
  cy.get(element).contains('span', label).parent().click()
}

/**
 * Enter `input` into a typeahead `element` and select the first result
 */
export const selectFirstTypeaheadOption = ({ element, input }) => {
  cy.get(element).type(input)
  cy.get(element).find('[data-test="typeahead-menu-option"]').first().click()
  cy.get(element).find('input').blur()
}

/**
 * Removes a specific chip based on the data-value
 */
export const removeChip = (dataValue) => {
  cy.get('[data-test=filter-chips]').find(`[data-value="${dataValue}"]`).click()
}

/**
 * Adds a date to the any given date input field
 */
export const inputDateValue = ({ element, value }) => {
  cy.get(element).type(value)
}

/**
 * Adds a date object of {day, month, year} to the relevent input fields
 */
export const clearAndInputDateValueObject = ({ element, value }) => {
  cy.wrap(element).then(($el) => {
    cy.wrap($el).find('[data-test$="-day"]').clear().type(value.day.toString())
    cy.wrap($el)
      .find('[data-test$="-month"]')
      .clear()
      .type(value.month.toString())
    cy.wrap($el)
      .find('[data-test$="-year"]')
      .clear()
      .type(value.year.toString())
  })
}

/**
 * Clears and Types a value in an input field
 */
export const clearAndTypeInput = ({ element, value }) => {
  cy.wrap(element).then(($el) => {
    cy.wrap($el)
      .find('input')
      .clear()
      .type(value ?? '')
  })
}

/**
 * Clears and Types a value in an textarea field
 */
export const clearAndTypeTextArea = ({ element, value }) => {
  cy.wrap(element).then(($el) => {
    cy.wrap($el)
      .find('textarea')
      .clear()
      .type(value ?? '')
  })
}

/**
 * Remove the first item in a multi-select Typeahead
 */
export const removeFirstTypeaheadItem = () => {
  cy.get('[data-test="typeahead-chip"]').eq(0).click()
}

/**
 * Click on a button
 * @param {*} buttonText The text of the button you want to click
 */
export const clickButton = (buttonText) => {
  cy.contains('button', buttonText).click()
}

/**
 * Click on a Cancel anchor
 */
export const clickCancelLink = () => {
  cy.get('[data-test="cancel-button"]').click()
}
<|endoftext|>


<commit_before>const express = require('express')
const cors = require('cors')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')
const slack = require('slack')

const port = process.env.PORT || 3000
const token = process.env.SLACK_TOKEN

const ohsTeam = {
  U1CTUKMT9: {}, // Zoé
  // U40MZ9EL9: {}, // Tugba
  U0BU8V7N0: {}, // Trev
  // U2NC0H2BS: {}, // Temi
  U0BUEB5BK: {}, // Paolo
  U0BU8SV4K: {}, // Michele
  U1KU01R4K: {}, // Kristina
  U162CB0LC: {}, // James
  U0C160N8N: {}, // Christean
  U0BUPA30R: {}, // Chris
  U1CU5P4H0: {}, // Anna
}

const orbitTeam = {
  U2L4F0AF3: {}, // James
}

const team = orbitTeam

const slapp = Slapp({
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})

// fetch users and store as team members

const updateMembers = slack.users.list({ token }, (err, { members }) => {
  if (err) throw new Error(err)

  console.log(`Fetched ${members.length} users`)
  console.log(members.map(({ id }) => id).join(', '))

  members.forEach((member) => {
    if (!team[member.id]) return
    team[member.id] = member
  })
})

updateMembers()

setInterval(updateMembers, 10000)

// commands

slapp.command('/location', '(.*)', (msg, text, location) => {
  if (!team[msg.body.user_id]) return
  team[msg.body.user_id].location = location
  msg.respond(`Your location has been set as _${location}_`)
})

// public api

const app = slapp.attachToExpress(express())

app.use(cors())

app.get('/', (req, res) => {
  res.send('Hello world.')
})

app.get('/team', (req, res) => {
  res.send(team)
})

// start server

app.listen(port, (err) => {
  if (err) return console.error(err)
  console.log(`Listening on port ${port}`)
})
<commit_msg>Fix bug<commit_after>const express = require('express')
const cors = require('cors')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')
const slack = require('slack')

const port = process.env.PORT || 3000
const token = process.env.SLACK_TOKEN

const ohsTeam = {
  U1CTUKMT9: {}, // Zoé
  // U40MZ9EL9: {}, // Tugba
  U0BU8V7N0: {}, // Trev
  // U2NC0H2BS: {}, // Temi
  U0BUEB5BK: {}, // Paolo
  U0BU8SV4K: {}, // Michele
  U1KU01R4K: {}, // Kristina
  U162CB0LC: {}, // James
  U0C160N8N: {}, // Christean
  U0BUPA30R: {}, // Chris
  U1CU5P4H0: {}, // Anna
}

const orbitTeam = {
  U2L4F0AF3: {}, // James
}

const team = orbitTeam

const slapp = Slapp({
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})

// fetch users and store as team members

const updateMembers = () => slack.users.list({ token }, (err, { members }) => {
  if (err) throw new Error(err)

  console.log(`Fetched ${members.length} users`)
  console.log(members.map(({ id }) => id).join(', '))

  members.forEach((member) => {
    if (!team[member.id]) return
    team[member.id] = member
  })
})

updateMembers()

setInterval(updateMembers, 10000)

// commands

slapp.command('/location', '(.*)', (msg, text, location) => {
  if (!team[msg.body.user_id]) return
  team[msg.body.user_id].location = location
  msg.respond(`Your location has been set as _${location}_`)
})

// public api

const app = slapp.attachToExpress(express())

app.use(cors())

app.get('/', (req, res) => {
  res.send('Hello world.')
})

app.get('/team', (req, res) => {
  res.send(team)
})

// start server

app.listen(port, (err) => {
  if (err) return console.error(err)
  console.log(`Listening on port ${port}`)
})
<|endoftext|>


<commit_before>var path = require('path');
// var Promise = require('promise');
var minimatch = require("minimatch");
var postcss = require('postcss');

module.exports = exports = function(options) {
  var defaults = {
    pattern: ['**/*.css', '!**/_*/*', '!**/_*'],
    plugins: {},
    map: {inline: true}
  }
  options = options ? Object.assign(defaults, options) : defaults;
  options.pattern = Array.isArray(options.pattern) ? options.pattern : [options.pattern];
  var plugin = null;
  var plugins = [];
  var pluginKeys = Object.keys(options.plugins);

  if(typeof options.plugins == 'object' && pluginKeys.length)
    for(var i = 0, name; name = pluginKeys[i++];) {
      var args = options.plugins[name];
      // console.log(name, args);
      plugin = require(name);
      plugins.push(plugin(args));
    }
  else
    throw new Error('[metalsmith-with-postcss] You must provide some PostCSS plugins.');

  var processor = postcss(plugins);

  return function metalsmithPostcss(files, metalsmith, done){
    var allKeys = Object.keys(files);
    var actKeys = [];
    var remKeys = [];
    var keys = null;
    var promises = [];

    for(var i = 0, pat; pat = options.pattern[i++];) {
      if(pat[0] != '!') {
        keys = allKeys.filter(minimatch.filter(pat, { matchBase: true }));
        actKeys = actKeys.concat(keys);
        remKeys = remKeys.length ? remKeys.filter(minimatch.filter('!' + pat, { matchBase: true })) : remKeys;
        // remKeys = remKeys.concat(keys);
      }
      else if(actKeys.length) {
        actKeys = actKeys.filter(minimatch.filter(pat, { matchBase: true }));
        keys = allKeys.filter(minimatch.filter(pat.slice(1), { matchBase: true }));
        remKeys = remKeys.concat(keys);
      }
    }

    for(var i = 0, key; key = actKeys[i++];) {
      var file = files[key];
      var css = file.contents.toString();

      var promise = processor.process(css, {
        from: path.join(metalsmith._source, key),
        to: path.join(metalsmith._destination, key),
        map: options.map
      })
      .then((function(file, result) {
        // console.log(file, '\n-------------------------------------------');
        file.contents = new Buffer(result.css);
      }).bind(null, file));

      promises.push(promise);
    }

    Promise.all(promises)
      .then(function(results) {
        for(var i = 0, key; key = remKeys[i++];) {
          delete files[key];
        }
        // console.log(actKeys, remKeys, Object.keys(files));
        done();
      })
      .catch(function(err) {
        done(new Error('[metalsmith-with-postcss] Error during postcss processing: ' + JSON.stringify(err)));
      });

  };
}<commit_msg>Added removeExcluded option.<commit_after>var path = require('path');
var minimatch = require("minimatch");
var postcss = require('postcss');

module.exports = exports = function(options) {
  var defaults = {
    pattern: ['**/*.css', '!**/_*/*', '!**/_*'],
    plugins: {},
    map: {inline: true},
    removeExcluded: false
  }
  options = options ? Object.assign(defaults, options) : defaults;
  options.pattern = Array.isArray(options.pattern) ? options.pattern : [options.pattern];
  var plugin = null;
  var plugins = [];
  var pluginKeys = Object.keys(options.plugins);

  if(typeof options.plugins == 'object' && pluginKeys.length)
    for(var i = 0, name; name = pluginKeys[i++];) {
      var args = options.plugins[name];
      // console.log(name, args);
      plugin = require(name);
      plugins.push(plugin(args));
    }
  else
    throw new Error('[metalsmith-with-postcss] You must provide some PostCSS plugins.');

  var processor = postcss(plugins);

  return function metalsmithPostcss(files, metalsmith, done){
    var allKeys = Object.keys(files);
    var actKeys = [];
    var remKeys = [];
    var keys = null;
    var promises = [];

    for(var i = 0, pat; pat = options.pattern[i++];) {
      if(pat[0] != '!') {
        keys = allKeys.filter(minimatch.filter(pat, { matchBase: true }));
        actKeys = actKeys.concat(keys);
        remKeys = remKeys.length ? remKeys.filter(minimatch.filter('!' + pat, { matchBase: true })) : remKeys;
        // remKeys = remKeys.concat(keys);
      }
      else if(actKeys.length) {
        actKeys = actKeys.filter(minimatch.filter(pat, { matchBase: true }));
        keys = allKeys.filter(minimatch.filter(pat.slice(1), { matchBase: true }));
        remKeys = remKeys.concat(keys);
      }
    }

    for(var i = 0, key; key = actKeys[i++];) {
      var file = files[key];
      var css = file.contents.toString();

      var promise = processor.process(css, {
        from: path.join(metalsmith._source, key),
        to: path.join(metalsmith._destination, key),
        map: options.map
      })
      .then((function(file, result) {
        // console.log(file, '\n-------------------------------------------');
        file.contents = new Buffer(result.css);
      }).bind(null, file));

      promises.push(promise);
    }

    Promise.all(promises)
      .then(function(results) {
        if(options.removeExcluded) {
          for(var i = 0, key; key = remKeys[i++];) {
            delete files[key];
          }
        }
        // console.log(actKeys, remKeys, Object.keys(files));
        done();
      })
      .catch(function(err) {
        done(new Error('[metalsmith-with-postcss] Error during postcss processing: ' + JSON.stringify(err)));
      });

  };
}<|endoftext|>