<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 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 Input field.  Used for editable titles, variables, etc.
 * This is an abstract class that defines the UI on the block.  Actual
 * instances would be Blockly.FieldTextInput, Blockly.FieldDropdown, etc.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.Field');

goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.math.Size');
goog.require('goog.style');
goog.require('goog.userAgent');

/**
 * Class for an editable field.
 * @param {string} text The initial content of the field.
 * @constructor
 */
Blockly.Field = function(text) {
  this.size_ = new goog.math.Size(0, 25);
  this.setText(text);
};

/**
 * Maximum length of text to display before adding an ellipsis.
 */
Blockly.Field.prototype.maxDisplayLength = 50;

/**
 * Block this field is attached to.  Starts as null, then in set in init.
 * @private
 */
Blockly.Field.prototype.sourceBlock_ = null;

/**
 * Is the field visible, or hidden due to the block being collapsed?
 * @private
 */
Blockly.Field.prototype.visible_ = true;

/**
 * Change handler called when user edits an editable field.
 * @private
 */
Blockly.Field.prototype.changeHandler_ = null;

/**
 * Non-breaking space.
 */
Blockly.Field.NBSP = '\u00A0';

/**
 * Editable fields are saved by the XML renderer, non-editable fields are not.
 */
Blockly.Field.prototype.EDITABLE = true;

/**
 * Install this field on a block.
 * @param {!Blockly.Block} block The block containing this field.
 */
Blockly.Field.prototype.init = function(block) {
  if (this.sourceBlock_) {
    // Field has already been initialized once.
    return;
  }
  this.sourceBlock_ = block;
  // Build the DOM.
  this.fieldGroup_ = Blockly.createSvgElement('g', {}, null);
  if (!this.visible_) {
    this.fieldGroup_.style.display = 'none';
  }
  this.borderRect_ = Blockly.createSvgElement('rect',
      {'rx': 4,
       'ry': 4,
       'x': -Blockly.BlockSvg.SEP_SPACE_X / 2,
       'y': -12,
       'height': 16}, this.fieldGroup_, this.sourceBlock_.workspace);
  this.textElement_ = Blockly.createSvgElement('text',
      {'class': 'blocklyText'}, this.fieldGroup_);

  this.updateEditable();
  block.getSvgRoot().appendChild(this.fieldGroup_);
  this.mouseUpWrapper_ =
      Blockly.bindEvent_(this.fieldGroup_, 'mouseup', this, this.onMouseUp_);
  // Force a render.
  this.updateTextNode_();
};

/**
 * Dispose of all DOM objects belonging to this editable field.
 */
Blockly.Field.prototype.dispose = function() {
  if (this.mouseUpWrapper_) {
    Blockly.unbindEvent_(this.mouseUpWrapper_);
    this.mouseUpWrapper_ = null;
  }
  this.sourceBlock_ = null;
  goog.dom.removeNode(this.fieldGroup_);
  this.fieldGroup_ = null;
  this.textElement_ = null;
  this.borderRect_ = null;
  this.changeHandler_ = null;
};

/**
 * Add or remove the UI indicating if this field is editable or not.
 */
Blockly.Field.prototype.updateEditable = function() {
  if (!this.EDITABLE || !this.sourceBlock_) {
    return;
  }
  if (this.sourceBlock_.isEditable()) {
    Blockly.addClass_(/** @type {!Element} */ (this.fieldGroup_),
                      'blocklyEditableText');
    Blockly.removeClass_(/** @type {!Element} */ (this.fieldGroup_),
                         'blocklyNoNEditableText');
    this.fieldGroup_.style.cursor = this.CURSOR;
  } else {
    Blockly.addClass_(/** @type {!Element} */ (this.fieldGroup_),
                      'blocklyNonEditableText');
    Blockly.removeClass_(/** @type {!Element} */ (this.fieldGroup_),
                         'blocklyEditableText');
    this.fieldGroup_.style.cursor = '';
  }
};

/**
 * Gets whether this editable field is visible or not.
 * @return {boolean} True if visible.
 */
Blockly.Field.prototype.isVisible = function() {
  return this.visible_;
};

/**
 * Sets whether this editable field is visible or not.
 * @param {boolean} visible True if visible.
 */
Blockly.Field.prototype.setVisible = function(visible) {
  if (this.visible_ == visible) {
    return;
  }
  this.visible_ = visible;
  var root = this.getSvgRoot();
  if (root) {
    root.style.display = visible ? 'block' : 'none';
    this.render_();
  }
};

/**
 * Sets a new change handler for editable fields.
 * @param {Function} handler New change handler, or null.
 */
Blockly.Field.prototype.setChangeHandler = function(handler) {
  this.changeHandler_ = handler;
};

/**
 * Gets the group element for this editable field.
 * Used for measuring the size and for positioning.
 * @return {!Element} The group element.
 */
Blockly.Field.prototype.getSvgRoot = function() {
  return /** @type {!Element} */ (this.fieldGroup_);
};

/**
 * Draws the border with the correct width.
 * Saves the computed width in a property.
 * @private
 */
Blockly.Field.prototype.render_ = function() {
  if (this.visible_ && this.textElement_) {
    try {
      var width = this.textElement_.getComputedTextLength();
    } catch (e) {
      // MSIE 11 is known to throw "Unexpected call to method or property
      // access." if Blockly is hidden.
      var width = this.textElement_.textContent.length * 8;
    }
    if (this.borderRect_) {
      this.borderRect_.setAttribute('width',
          width + Blockly.BlockSvg.SEP_SPACE_X);
    }
  } else {
    var width = 0;
  }
  this.size_.width = width;
};

/**
 * Returns the height and width of the field.
 * @return {!goog.math.Size} Height and width.
 */
Blockly.Field.prototype.getSize = function() {
  if (!this.size_.width) {
    this.render_();
  }
  return this.size_;
};

/**
 * Returns the height and width of the field,
 * accounting for the workspace scaling.
 * @return {!Object} Height and width.
 */
Blockly.Field.prototype.getScaledBBox_ = function() {
  var bBox = this.borderRect_.getBBox();
  bBox.width *= this.sourceBlock_.workspace.scale;
  bBox.height *= this.sourceBlock_.workspace.scale;
  return bBox;
};

/**
 * Get the text from this field.
 * @return {string} Current text.
 */
Blockly.Field.prototype.getText = function() {
  return this.text_;
};

/**
 * Set the text in this field.  Trigger a rerender of the source block.
 * @param {*} text New text.
 */
Blockly.Field.prototype.setText = function(text) {
  if (text === null) {
    // No change if null.
    return;
  }
  text = String(text);
  if (text === this.text_) {
    // No change.
    return;
  }
  this.text_ = text;
  this.updateTextNode_();

  if (this.sourceBlock_ && this.sourceBlock_.rendered) {
    this.sourceBlock_.render();
    this.sourceBlock_.bumpNeighbours_();
    this.sourceBlock_.workspace.fireChangeEvent();
  }
};

/**
 * Update the text node of this field to display the current text.
 * @private
 */
Blockly.Field.prototype.updateTextNode_ = function() {
  if (!this.textElement_) {
    // Not rendered yet.
    return;
  }
  var text = this.text_;
  if (text.length > this.maxDisplayLength) {
    // Truncate displayed string and add an ellipsis ('...').
    text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
  }
  // Empty the text element.
  goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
  // Replace whitespace with non-breaking spaces so the text doesn't collapse.
  text = text.replace(/\s/g, Blockly.Field.NBSP);
  if (this.sourceBlock_.RTL && text) {
    // The SVG is LTR, force text to be RTL.
    text += '\u200F';
  }
  if (!text) {
    // Prevent the field from disappearing if empty.
    text = Blockly.Field.NBSP;
  }
  var textNode = document.createTextNode(text);
  this.textElement_.appendChild(textNode);

  // Cached width is obsolete.  Clear it.
  this.size_.width = 0;
};

/**
 * By default there is no difference between the human-readable text and
 * the language-neutral values.  Subclasses (such as dropdown) may define this.
 * @return {string} Current text.
 */
Blockly.Field.prototype.getValue = function() {
  return this.getText();
};

/**
 * By default there is no difference between the human-readable text and
 * the language-neutral values.  Subclasses (such as dropdown) may define this.
 * @param {string} text New text.
 */
Blockly.Field.prototype.setValue = function(text) {
  this.setText(text);
};

/**
 * Handle a mouse up event on an editable field.
 * @param {!Event} e Mouse up event.
 * @private
 */
Blockly.Field.prototype.onMouseUp_ = function(e) {
  if ((goog.userAgent.IPHONE || goog.userAgent.IPAD) &&
      !goog.userAgent.isVersionOrHigher('537.51.2') &&
      e.layerX !== 0 && e.layerY !== 0) {
    // Old iOS spawns a bogus event on the next touch after a 'prompt()' edit.
    // Unlike the real events, these have a layerX and layerY set.
    return;
  } else if (Blockly.isRightButton(e)) {
    // Right-click.
    return;
  } else if (Blockly.dragMode_ == 2) {
    // Drag operation is concluding.  Don't open the editor.
    return;
  } else if (this.sourceBlock_.isEditable()) {
    // Non-abstract sub-classes must define a showEditor_ method.
    this.showEditor_();
  }
};

/**
 * Change the tooltip text for this field.
 * @param {string|!Element} newTip Text for tooltip or a parent element to
 *     link to for its tooltip.
 */
Blockly.Field.prototype.setTooltip = function(newTip) {
  // Non-abstract sub-classes may wish to implement this.  See FieldLabel.
};

/**
 * Return the absolute coordinates of the top-left corner of this field.
 * The origin (0,0) is the top-left corner of the page body.
 * @return {{!goog.math.Coordinate}} Object with .x and .y properties.
 * @private
 */
Blockly.Field.prototype.getAbsoluteXY_ = function() {
  return goog.style.getPageOffset(this.borderRect_);
};
<commit_msg>Update getScaledBBox_ to return new object to fix issue in IE.<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 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 Input field.  Used for editable titles, variables, etc.
 * This is an abstract class that defines the UI on the block.  Actual
 * instances would be Blockly.FieldTextInput, Blockly.FieldDropdown, etc.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.Field');

goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.math.Size');
goog.require('goog.style');
goog.require('goog.userAgent');

/**
 * Class for an editable field.
 * @param {string} text The initial content of the field.
 * @constructor
 */
Blockly.Field = function(text) {
  this.size_ = new goog.math.Size(0, 25);
  this.setText(text);
};

/**
 * Maximum length of text to display before adding an ellipsis.
 */
Blockly.Field.prototype.maxDisplayLength = 50;

/**
 * Block this field is attached to.  Starts as null, then in set in init.
 * @private
 */
Blockly.Field.prototype.sourceBlock_ = null;

/**
 * Is the field visible, or hidden due to the block being collapsed?
 * @private
 */
Blockly.Field.prototype.visible_ = true;

/**
 * Change handler called when user edits an editable field.
 * @private
 */
Blockly.Field.prototype.changeHandler_ = null;

/**
 * Non-breaking space.
 */
Blockly.Field.NBSP = '\u00A0';

/**
 * Editable fields are saved by the XML renderer, non-editable fields are not.
 */
Blockly.Field.prototype.EDITABLE = true;

/**
 * Install this field on a block.
 * @param {!Blockly.Block} block The block containing this field.
 */
Blockly.Field.prototype.init = function(block) {
  if (this.sourceBlock_) {
    // Field has already been initialized once.
    return;
  }
  this.sourceBlock_ = block;
  // Build the DOM.
  this.fieldGroup_ = Blockly.createSvgElement('g', {}, null);
  if (!this.visible_) {
    this.fieldGroup_.style.display = 'none';
  }
  this.borderRect_ = Blockly.createSvgElement('rect',
      {'rx': 4,
       'ry': 4,
       'x': -Blockly.BlockSvg.SEP_SPACE_X / 2,
       'y': -12,
       'height': 16}, this.fieldGroup_, this.sourceBlock_.workspace);
  this.textElement_ = Blockly.createSvgElement('text',
      {'class': 'blocklyText'}, this.fieldGroup_);

  this.updateEditable();
  block.getSvgRoot().appendChild(this.fieldGroup_);
  this.mouseUpWrapper_ =
      Blockly.bindEvent_(this.fieldGroup_, 'mouseup', this, this.onMouseUp_);
  // Force a render.
  this.updateTextNode_();
};

/**
 * Dispose of all DOM objects belonging to this editable field.
 */
Blockly.Field.prototype.dispose = function() {
  if (this.mouseUpWrapper_) {
    Blockly.unbindEvent_(this.mouseUpWrapper_);
    this.mouseUpWrapper_ = null;
  }
  this.sourceBlock_ = null;
  goog.dom.removeNode(this.fieldGroup_);
  this.fieldGroup_ = null;
  this.textElement_ = null;
  this.borderRect_ = null;
  this.changeHandler_ = null;
};

/**
 * Add or remove the UI indicating if this field is editable or not.
 */
Blockly.Field.prototype.updateEditable = function() {
  if (!this.EDITABLE || !this.sourceBlock_) {
    return;
  }
  if (this.sourceBlock_.isEditable()) {
    Blockly.addClass_(/** @type {!Element} */ (this.fieldGroup_),
                      'blocklyEditableText');
    Blockly.removeClass_(/** @type {!Element} */ (this.fieldGroup_),
                         'blocklyNoNEditableText');
    this.fieldGroup_.style.cursor = this.CURSOR;
  } else {
    Blockly.addClass_(/** @type {!Element} */ (this.fieldGroup_),
                      'blocklyNonEditableText');
    Blockly.removeClass_(/** @type {!Element} */ (this.fieldGroup_),
                         'blocklyEditableText');
    this.fieldGroup_.style.cursor = '';
  }
};

/**
 * Gets whether this editable field is visible or not.
 * @return {boolean} True if visible.
 */
Blockly.Field.prototype.isVisible = function() {
  return this.visible_;
};

/**
 * Sets whether this editable field is visible or not.
 * @param {boolean} visible True if visible.
 */
Blockly.Field.prototype.setVisible = function(visible) {
  if (this.visible_ == visible) {
    return;
  }
  this.visible_ = visible;
  var root = this.getSvgRoot();
  if (root) {
    root.style.display = visible ? 'block' : 'none';
    this.render_();
  }
};

/**
 * Sets a new change handler for editable fields.
 * @param {Function} handler New change handler, or null.
 */
Blockly.Field.prototype.setChangeHandler = function(handler) {
  this.changeHandler_ = handler;
};

/**
 * Gets the group element for this editable field.
 * Used for measuring the size and for positioning.
 * @return {!Element} The group element.
 */
Blockly.Field.prototype.getSvgRoot = function() {
  return /** @type {!Element} */ (this.fieldGroup_);
};

/**
 * Draws the border with the correct width.
 * Saves the computed width in a property.
 * @private
 */
Blockly.Field.prototype.render_ = function() {
  if (this.visible_ && this.textElement_) {
    try {
      var width = this.textElement_.getComputedTextLength();
    } catch (e) {
      // MSIE 11 is known to throw "Unexpected call to method or property
      // access." if Blockly is hidden.
      var width = this.textElement_.textContent.length * 8;
    }
    if (this.borderRect_) {
      this.borderRect_.setAttribute('width',
          width + Blockly.BlockSvg.SEP_SPACE_X);
    }
  } else {
    var width = 0;
  }
  this.size_.width = width;
};

/**
 * Returns the height and width of the field.
 * @return {!goog.math.Size} Height and width.
 */
Blockly.Field.prototype.getSize = function() {
  if (!this.size_.width) {
    this.render_();
  }
  return this.size_;
};

/**
 * Returns the height and width of the field,
 * accounting for the workspace scaling.
 * @return {!Object} Height and width.
 */
Blockly.Field.prototype.getScaledBBox_ = function() {
  var bBox = this.borderRect_.getBBox();
  // Create new object, as getBBox can return an uneditable SVGRect.
  return {width: bBox.width * this.sourceBlock_.workspace.scale,
          height: bBox.height * this.sourceBlock_.workspace.scale};
};

/**
 * Get the text from this field.
 * @return {string} Current text.
 */
Blockly.Field.prototype.getText = function() {
  return this.text_;
};

/**
 * Set the text in this field.  Trigger a rerender of the source block.
 * @param {*} text New text.
 */
Blockly.Field.prototype.setText = function(text) {
  if (text === null) {
    // No change if null.
    return;
  }
  text = String(text);
  if (text === this.text_) {
    // No change.
    return;
  }
  this.text_ = text;
  this.updateTextNode_();

  if (this.sourceBlock_ && this.sourceBlock_.rendered) {
    this.sourceBlock_.render();
    this.sourceBlock_.bumpNeighbours_();
    this.sourceBlock_.workspace.fireChangeEvent();
  }
};

/**
 * Update the text node of this field to display the current text.
 * @private
 */
Blockly.Field.prototype.updateTextNode_ = function() {
  if (!this.textElement_) {
    // Not rendered yet.
    return;
  }
  var text = this.text_;
  if (text.length > this.maxDisplayLength) {
    // Truncate displayed string and add an ellipsis ('...').
    text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
  }
  // Empty the text element.
  goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
  // Replace whitespace with non-breaking spaces so the text doesn't collapse.
  text = text.replace(/\s/g, Blockly.Field.NBSP);
  if (this.sourceBlock_.RTL && text) {
    // The SVG is LTR, force text to be RTL.
    text += '\u200F';
  }
  if (!text) {
    // Prevent the field from disappearing if empty.
    text = Blockly.Field.NBSP;
  }
  var textNode = document.createTextNode(text);
  this.textElement_.appendChild(textNode);

  // Cached width is obsolete.  Clear it.
  this.size_.width = 0;
};

/**
 * By default there is no difference between the human-readable text and
 * the language-neutral values.  Subclasses (such as dropdown) may define this.
 * @return {string} Current text.
 */
Blockly.Field.prototype.getValue = function() {
  return this.getText();
};

/**
 * By default there is no difference between the human-readable text and
 * the language-neutral values.  Subclasses (such as dropdown) may define this.
 * @param {string} text New text.
 */
Blockly.Field.prototype.setValue = function(text) {
  this.setText(text);
};

/**
 * Handle a mouse up event on an editable field.
 * @param {!Event} e Mouse up event.
 * @private
 */
Blockly.Field.prototype.onMouseUp_ = function(e) {
  if ((goog.userAgent.IPHONE || goog.userAgent.IPAD) &&
      !goog.userAgent.isVersionOrHigher('537.51.2') &&
      e.layerX !== 0 && e.layerY !== 0) {
    // Old iOS spawns a bogus event on the next touch after a 'prompt()' edit.
    // Unlike the real events, these have a layerX and layerY set.
    return;
  } else if (Blockly.isRightButton(e)) {
    // Right-click.
    return;
  } else if (Blockly.dragMode_ == 2) {
    // Drag operation is concluding.  Don't open the editor.
    return;
  } else if (this.sourceBlock_.isEditable()) {
    // Non-abstract sub-classes must define a showEditor_ method.
    this.showEditor_();
  }
};

/**
 * Change the tooltip text for this field.
 * @param {string|!Element} newTip Text for tooltip or a parent element to
 *     link to for its tooltip.
 */
Blockly.Field.prototype.setTooltip = function(newTip) {
  // Non-abstract sub-classes may wish to implement this.  See FieldLabel.
};

/**
 * Return the absolute coordinates of the top-left corner of this field.
 * The origin (0,0) is the top-left corner of the page body.
 * @return {{!goog.math.Coordinate}} Object with .x and .y properties.
 * @private
 */
Blockly.Field.prototype.getAbsoluteXY_ = function() {
  return goog.style.getPageOffset(this.borderRect_);
};
<|endoftext|>


<commit_before>import React from 'react';
import Portal from 'react-portal';
import { styled } from 'olymp';
import { Menu as AntMenu } from 'antd';

export const Menu = styled(({ theme }) => ({
  position: 'fixed',
  top: 0,
  zIndex: 10,
  left: '50%',
  transform: 'translateX(-50%)',
  background: theme.dark,
  borderBottom: 0,
  '> li': {
    padding: 0,
    color: `${theme.light}!important`,
    '> .ant-menu-submenu-title': {
      padding: 0,
    },
  },
  '> li:hover': {
    color: theme.light1,
  },
}), AntMenu, p => p);

export const Button = styled(() => ({
  paddingX: 20,
}), ({ className, onMouseDown, children, innerKey, ...props }) => (

    {children}

), p => p);

export default styled(({ theme }) => ({
  /*width: '100%',
  display: 'flex',
  justifyContent: 'center',*/
}), (props) => {
  const { isOpened, className, children } = props;
  return (
    <Portal isOpened={!!isOpened}>
      <Menu mode="horizontal">
        {children}
      </Menu>
    </Portal>
  );
}, p => p);
<commit_msg>was hälst du von einer hellen Toolbar? Ich finde die schwarze so erdrückend...<commit_after>import React from 'react';
import Portal from 'react-portal';
import { styled } from 'olymp';
import { border } from 'olymp-fela';
import { Menu as AntMenu } from 'antd';

export const Menu = styled(({ theme }) => ({
  position: 'fixed',
  top: 0,
  zIndex: 10,
  left: '50%',
  transform: 'translateX(-50%)',
  background: theme.light1,
  borderBottom: 0,
  boxShadow: theme.shadow,
  border: border(theme),
  borderTop: 0,
  '> li': {
    padding: 0,
    color: `${theme.dark1}!important`,
    '> .ant-menu-submenu-title': {
      padding: 0,
    },
  },
  '> li:hover': {
    color: theme.light1,
  },
}), AntMenu, p => p);

export const Button = styled(() => ({
  paddingX: 20,
}), ({ className, onMouseDown, children, innerKey, ...props }) => (

    {children}

), p => p);

export default styled(({ theme }) => ({
  /*width: '100%',
  display: 'flex',
  justifyContent: 'center',*/
}), (props) => {
  const { isOpened, className, children } = props;
  return (
    <Portal isOpened={!!isOpened}>
      <Menu mode="horizontal">
        {children}
      </Menu>
    </Portal>
  );
}, p => p);
<|endoftext|>


<commit_before>const URL_REGEXS = [
    /abcnews.go.com\/.+/i,
    /arstechnica.com\/.+/i,
    /bbc.co.uk\/.+/i,
    /bbc.com\/.+/i,
    /business-standard.com\/.+/i,
    /cnn.com\/.+/i,
    /economist.com\/.+/i,
    /guardian.co.uk\/.+/i,
    /theguardian.com\/.+/i,
    /hollywoodreporter.com\/.+/i,
    /huffingtonpost.com\/.+/i,
    /irishtimes.com\/.+/i,
    /independent.co.uk\/.+/i,
    /npr.org\/.+/i,
    /newsweek.com\/.+/i,
    /nytimes.com\/.+/i,
    /politico.com\/.+/i,
    /rollingstone.com\/.+/i,
    /spiegel.de\/.+/i,
    /time.com\/.+/i,
    /theatlantic.com\/.+/i,
    /variety.com\/.+/i,
    /washingtonpost.com\/.+/i,
    /wired.com\/.+/i,
    /wsj.com\/.+/i,
]

const SIMILARITY_URL = 'https://tools.wmflabs.org/similarity/search';

let SuggestionsCache = new (function() {
  const MAX_CACHE_SIZE = 10;

  let cache = [];

  this.get = key => {
    for (let i = 0; i < cache.length; i++) {
      let entry = cache[i];
      if (entry.key === key) {
        return entry.value;
      }
    }
    return undefined;
  };

  this.set = (key, value) => {
    if (cache.length == MAX_CACHE_SIZE) {
      cache.shift();
    }
    cache.push({key: key, value: value});
  };
});

function getDocumentContents(callback) {
  let script = 'document.documentElement.outerHTML';
  chrome.tabs.executeScript({
    code: script
  }, (result) => { callback(result[0]); });
}

function fetchSimilarArticles(html, url, callback) {
  let formData = new FormData();
  formData.append('html', html);
  formData.append('url', url);
  let o = {method: 'POST', mode: 'cors', body: formData};
  window.fetch(SIMILARITY_URL, o).then(
    (response) => { return response.json(); }).then(callback);
}

function getSuggestionsForTab(tab, callback, options) {
  if (!tab.url) return;
  if (!options || options.canUseCache) {
    let cached = SuggestionsCache.get(tab.url);
    if (cached) {
      callback(cached);
      return;
    }
  }

  let match = false;
  for (let i = 0; i < URL_REGEXS.length; i++) {
    if (tab.url.match(URL_REGEXS[i])) {
      match = true;
    }
  }
  if (!match) return;
  getDocumentContents((html) => {
    fetchSimilarArticles(html, tab.url, suggestions => {
      SuggestionsCache.set(tab.url, suggestions);
      callback(suggestions);
    });
  });
}

chrome.tabs.onActivated.addListener((activeInfo) => {
  chrome.tabs.get(activeInfo.tabId, (tab) => {
    getSuggestionsForTab(tab, (articles) => {
      if (articles.length) {
        chrome.pageAction.show(tab.id);
      } else {
        chrome.pageAction.hide(tab.id);
      }
    });
  });
});

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  chrome.pageAction.hide(tab.id);
  if (changeInfo.status == 'complete') {
    getSuggestionsForTab(tab, (articles) => {
      if (articles.length) {
        chrome.pageAction.show(tab.id);
      }
    }, {canUseCache: false});
  }
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
    getSuggestionsForTab(tabs[0], sendResponse);
  });
  return true;
});
<commit_msg>Don't assume chrome.tabs.onUpdated fires once per document.<commit_after>const URL_REGEXS = [
    /abcnews.go.com\/.+/i,
    /arstechnica.com\/.+/i,
    /bbc.co.uk\/.+/i,
    /bbc.com\/.+/i,
    /business-standard.com\/.+/i,
    /cnn.com\/.+/i,
    /economist.com\/.+/i,
    /guardian.co.uk\/.+/i,
    /theguardian.com\/.+/i,
    /hollywoodreporter.com\/.+/i,
    /huffingtonpost.com\/.+/i,
    /irishtimes.com\/.+/i,
    /independent.co.uk\/.+/i,
    /npr.org\/.+/i,
    /newsweek.com\/.+/i,
    /nytimes.com\/.+/i,
    /politico.com\/.+/i,
    /rollingstone.com\/.+/i,
    /spiegel.de\/.+/i,
    /time.com\/.+/i,
    /theatlantic.com\/.+/i,
    /variety.com\/.+/i,
    /washingtonpost.com\/.+/i,
    /wired.com\/.+/i,
    /wsj.com\/.+/i,
]

const SIMILARITY_URL = 'https://tools.wmflabs.org/similarity/search';

let SuggestionsCache = new (function() {
  const MAX_CACHE_SIZE = 10;

  let cache = [];

  this.get = key => {
    for (let i = 0; i < cache.length; i++) {
      let entry = cache[i];
      if (entry.key === key) {
        return entry.value;
      }
    }
    return undefined;
  };

  this.set = (key, value) => {
    if (cache.length == MAX_CACHE_SIZE) {
      cache.shift();
    }
    cache.push({key: key, value: value});
  };
});

function getDocumentContents(callback) {
  let script = 'document.documentElement.outerHTML';
  chrome.tabs.executeScript({
    code: script
  }, (result) => { callback(result[0]); });
}

function fetchSimilarArticles(html, url, callback) {
  let formData = new FormData();
  formData.append('html', html);
  formData.append('url', url);
  let o = {method: 'POST', mode: 'cors', body: formData};
  window.fetch(SIMILARITY_URL, o).then(
    (response) => { return response.json(); }).then(callback);
}

function getSuggestionsForTab(tab, callback, options) {
  if (!tab.url) return;
  if (!options || options.canUseCache) {
    let cached = SuggestionsCache.get(tab.url);
    if (cached) {
      callback(cached);
      return;
    }
  }

  let match = false;
  for (let i = 0; i < URL_REGEXS.length; i++) {
    if (tab.url.match(URL_REGEXS[i])) {
      match = true;
    }
  }
  if (!match) return;
  getDocumentContents((html) => {
    fetchSimilarArticles(html, tab.url, suggestions => {
      SuggestionsCache.set(tab.url, suggestions);
      callback(suggestions);
    });
  });
}

chrome.tabs.onActivated.addListener((activeInfo) => {
  chrome.tabs.get(activeInfo.tabId, (tab) => {
    getSuggestionsForTab(tab, (articles) => {
      if (articles.length) {
        chrome.pageAction.show(tab.id);
      } else {
        chrome.pageAction.hide(tab.id);
      }
    });
  });
});

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  // Note: this event can happen multiple times, even after the page loads,
  // and changeInfo.status can even be undefined (despite being documented
  // as only ever being either 'loading' or 'complete').
  if (changeInfo.status == 'complete') {
    getSuggestionsForTab(tab, (articles) => {
      if (articles.length) {
        chrome.pageAction.show(tab.id);
      } else {
        chrome.pageAction.hide(tab.id);
      }
    }, {canUseCache: false});
  }
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
    getSuggestionsForTab(tabs[0], sendResponse);
  });
  return true;
});
<|endoftext|>


<commit_before>const shell = require('shelljs');

const toCopy = {
  readme: {
    source: './README.md',
    target: './dist/angular-auth-oidc-client',
  },
  license: {
    source: './LICENSE',
    target: './dist/angular-auth-oidc-client',
  },
  ngAdd: {
    source: './projects/schematics/src/ng-add/files/*',
    target: './dist/angular-auth-oidc-client/schematics/ng-add/files',
    createFirectoryFirst: true,
  },
  collectionJson: {
    source: './projects/schematics/src/collection.json',
    target: './dist/angular-auth-oidc-client/schematics',
  },
  jsonSchema: {
    source: './projects/schematics/src/ng-add/schema.json',
    target: './dist/angular-auth-oidc-client/schematics/ng-add/schema.json',
  },
};

shell.echo('Start copying files...');

Object.entries(toCopy).forEach(([_, value]) => {
  const { source, target, createFirectoryFirst } = value;

  if (!!createFirectoryFirst) {
    shell.mkdir('-p', `${target}`);
  }

  shell.echo(`Copying from ${source} to ${target}`);
  shell.cp('-r', `${source}`, `${target}`);
});

shell.echo('...DONE');
<commit_msg>copying package.json as well<commit_after>const shell = require('shelljs');

const toCopy = {
  readme: {
    source: './README.md',
    target: './dist/angular-auth-oidc-client',
  },
  license: {
    source: './LICENSE',
    target: './dist/angular-auth-oidc-client',
  },
  ngAdd: {
    source: './projects/schematics/src/ng-add/files/*',
    target: './dist/angular-auth-oidc-client/schematics/ng-add/files',
    createDirectoryFirst: true,
  },
  collectionJson: {
    source: './projects/schematics/src/collection.json',
    target: './dist/angular-auth-oidc-client/schematics',
  },
  jsonSchema: {
    source: './projects/schematics/src/ng-add/schema.json',
    target: './dist/angular-auth-oidc-client/schematics/ng-add/schema.json',
  },
  schematicsPackageJson: {
    source: './projects/schematics/package.json',
    target: './dist/angular-auth-oidc-client/schematics/package.json',
  },
};

shell.echo('Start copying files...');

Object.entries(toCopy).forEach(([_, value]) => {
  const { source, target, createDirectoryFirst } = value;

  if (!!createDirectoryFirst) {
    shell.mkdir('-p', `${target}`);
  }

  shell.echo(`Copying from ${source} to ${target}`);
  shell.cp('-r', `${source}`, `${target}`);
});

shell.echo('...DONE');
<|endoftext|>


<commit_before>/* @flow */
import type { Action, Deps, Peer, DataConnection } from '../types';
import { Observable } from 'rxjs/Observable';
import { multiplayerActions } from '../cards/actions';

export const startup = (): Action => ({
  type: 'STARTUP',
});

export const started = (peer: Peer): Action => ({
  type: 'STARTED',
  payload: {
    peer,
  },
});

export const connecting = (id: string): Action => ({
  type: 'CONNECTING',
  payload: {
    id,
  },
});

export const connected = (connection: DataConnection): Action => ({
  type: 'CONNECTED',
  payload: {
    connection,
  },
});

export const close = (): Action => ({
  type: 'CLOSE',
});

export const send = (data: object): Action => ({
  type: 'SEND',
  payload: {
    data,
  },
});

export const receive = (data: object): Action => ({
  type: 'RECEIVE',
  payload: {
    data,
  },
});

export const setError = (error: string): Action => ({
  type: 'ERROR',
  payload: {
    error,
  },
});

const startupEpic = (
  action$: any,
  { peerjs }: Deps,
): Action => {
  if (typeof window === 'undefined') {
    return action$.mapTo({
      type: 'NOT_READY',
    });
  }

  const peer = new window.Peer({ key: peerjs });
  const peerConnection = Observable.fromPromise(
    new Promise((resolve) => {
      try {
        peer
          .on('open', () => resolve(peer))
          .on('error', err => resolve(setError(err.message)));
      } catch (err) {
        resolve(setError(err.message));
      }
    })
  );

  return action$
    .ofType('STARTUP')
    .mergeMap(() => peerConnection.map(started));

  // create observable that takes id and returns connetion
  // from connection, listen to connect or connected actions
  // both should return a completed connection, which starts listening for data
};

const listeningEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType('STARTED')
  .mergeMap(() => {
    const { peerjs: { peer } } = getState();

    if (!peer) {
      return Observable.of();
    }

    return Observable.fromPromise(
      new Promise((resolve) => {
        try {
          peer
            .on('connection', conn => resolve(connected(conn)))
            .on('error', err => resolve(setError(err.message)));
        } catch (err) {
          resolve(setError(err.message));
        }
      })
    )
    .takeUntil(action$.filter((action: Action) => action.type === 'CONNECTED'));
  });

const connectingEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType('CONNECTING')
  .mergeMap((action: Action) => {
    const { peerjs: { peer } } = getState();

    if (!peer) {
      return Observable.of();
    }

    return Observable.fromPromise(
      new Promise((resolve) => {
        try {
          const conn = peer.connect(
            action.payload.id
          );

          conn
            .on('open', () => resolve(connected(conn)))
            .on('error', err => resolve(setError(err.message)))
            .on('close', () => resolve(close()));
        } catch (err) {
          resolve(setError(err.message));
        }
      })
    );
  });

const sendActionEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType(...multiplayerActions)
  .mergeMap((action: Action) => {
    const { peerjs: { connection } } = getState();

    if (!connection) {
      return Observable.of();
    }

    try {
      console.log('sending data', action);
      // you are sending your actions,
      // which show as the opponent for the opponent!
      action.payload.isPlayer = false;
      connection.send(action);

      return Observable.of();
    } catch (err) {
      return Observable.of(setError(err.message));
    }
  });

const receiveActionEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType('CONNECTED')
  .mergeMap(() => {
    const { peerjs: { connection } } = getState();

    if (!connection) {
      return Observable.of();
    }

    return Observable.fromPromise(
      new Promise((resolve) => {
        console.log('listening');
        connection.on('data', (action) => {
          console.log('received data', action);
          resolve(action);
        });
      })
    );
  });

export const epics = [
  startupEpic,
  listeningEpic,
  connectingEpic,
  sendActionEpic,
  receiveActionEpic,
];
<commit_msg>Use Observable.fromEvent instead. WOW.<commit_after>/* @flow */
import type { Action, Deps, Peer, DataConnection } from '../types';
import { Observable } from 'rxjs/Observable';
import { multiplayerActions } from '../cards/actions';

export const startup = (): Action => ({
  type: 'STARTUP',
});

export const started = (peer: Peer): Action => ({
  type: 'STARTED',
  payload: {
    peer,
  },
});

export const connecting = (id: string): Action => ({
  type: 'CONNECTING',
  payload: {
    id,
  },
});

export const connected = (connection: DataConnection): Action => ({
  type: 'CONNECTED',
  payload: {
    connection,
  },
});

export const close = (): Action => ({
  type: 'CLOSE',
});

export const send = (data: object): Action => ({
  type: 'SEND',
  payload: {
    data,
  },
});

export const receive = (data: object): Action => ({
  type: 'RECEIVE',
  payload: {
    data,
  },
});

export const setError = (error: string): Action => ({
  type: 'ERROR',
  payload: {
    error,
  },
});

const startupEpic = (
  action$: any,
  { peerjs }: Deps,
): Action => {
  if (typeof window === 'undefined') {
    return action$.mapTo({
      type: 'NOT_READY',
    });
  }

  const peer = new window.Peer({ key: peerjs });
  const peerConnection = Observable.fromPromise(
    new Promise((resolve) => {
      try {
        peer
          .on('open', () => resolve(peer))
          .on('error', err => resolve(setError(err.message)));
      } catch (err) {
        resolve(setError(err.message));
      }
    })
  );

  return action$
    .ofType('STARTUP')
    .mergeMap(() => peerConnection.map(started));

  // create observable that takes id and returns connetion
  // from connection, listen to connect or connected actions
  // both should return a completed connection, which starts listening for data
};

const listeningEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType('STARTED')
  .mergeMap(() => {
    const { peerjs: { peer } } = getState();

    if (!peer) {
      return Observable.of();
    }

    return Observable.fromPromise(
      new Promise((resolve) => {
        try {
          peer
            .on('connection', conn => resolve(connected(conn)))
            .on('error', err => resolve(setError(err.message)));
        } catch (err) {
          resolve(setError(err.message));
        }
      })
    )
    .takeUntil(action$.filter((action: Action) => action.type === 'CONNECTED'));
  });

const connectingEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType('CONNECTING')
  .mergeMap((action: Action) => {
    const { peerjs: { peer } } = getState();

    if (!peer) {
      return Observable.of();
    }

    return Observable.fromPromise(
      new Promise((resolve) => {
        try {
          const conn = peer.connect(
            action.payload.id
          );

          conn
            .on('open', () => resolve(connected(conn)))
            .on('error', err => resolve(setError(err.message)))
            .on('close', () => resolve(close()));
        } catch (err) {
          resolve(setError(err.message));
        }
      })
    );
  });

const sendActionEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType(...multiplayerActions)
  .mergeMap((action: Action) => {
    const { peerjs: { connection } } = getState();

    if (!connection) {
      return Observable.of();
    }

    try {
      console.log('sending data', action);
        // you are sending your actions,
        // which show as the opponent for the opponent!
        action.payload.isPlayer = false;
        connection.send(action);

      return Observable.of();
    } catch (err) {
      return Observable.of(setError(err.message));
    }
  });

const receiveActionEpic = (
  action$: any,
  { getState },
): Action => action$
  .ofType('CONNECTED')
  .mergeMap(() => {
    const { peerjs: { connection } } = getState();

    if (!connection) {
      return Observable.of();
    }

    return Observable.fromEvent(connection, 'data');
    // const getEventHandlerAsObservable = Observable.fromEventPattern(
    //   handler => connection.on('data', handler),
    //   handler => console.log('attempting to close', handler),
    //   action => { console.log(action); return action; }
    // );

    // return getEventHandlerAsObservable;
  });

export const epics = [
  startupEpic,
  listeningEpic,
  connectingEpic,
  sendActionEpic,
  receiveActionEpic,
];
<|endoftext|>


<commit_before>import { withRouter } from 'next/router';

// @TODO - Play with style variable and convert to styles from Header.css stylesheet

const ActiveLink = ({ children, router, href }) => {
  const style = {
    textDecoration: 'none',
    marginRight: 10,
    color: router.pathname === href ? 'red' : 'black'
  }

  const handleClick = (e) => {
    e.preventDefault()
    router.push(href)
  }

  return (

      {children}

  )
}

export default withRouter(ActiveLink);
<commit_msg>added style to ActiveLink component<commit_after>import { withRouter } from 'next/router';

// @TODO - Play with style variable and convert to styles from Header.css stylesheet

const ActiveLink = ({ children, router, href }) => {
  const style = {
    textDecoration: 'none',
    textTransform: 'uppercase',
    fontSize: 20,
    fontWeight: 100,
    margin: 15,
    paddingBottom: 5,
    position: 'relative',
    marginRight: 10,
    color: router.pathname === href ? 'red' : 'white'
  }

  const handleClick = (e) => {
    e.preventDefault()
    router.push(href)
  }

  return (

      {children}

  )
}

export default withRouter(ActiveLink);
<|endoftext|>


<commit_before>window.showRealNames = () => {
	const storageKey = 'cachedNames';

	const getCachedUsers = cb => {
		chrome.storage.local.get(storageKey, data => cb(data[storageKey]));
	};

	const updateCachedUsers = users => {
		chrome.storage.local.set({[storageKey]: users});
	};

	const addUsersName = (user, name) => {
		const $usernameLinks = $(`.timeline-comment-header-text:not(.has-full-name) a[href="/${user}"]`);
		$usernameLinks.each((i, userLink) => {
			$(`<span class="comment-full-name">`).text(`${name} -`).insertAfter(userLink);
			$(userLink).closest('.timeline-comment-header-text').addClass('has-full-name');
		});
	};

	getCachedUsers((users = {}) => {
		const usersOnPage = Array.from($('.js-discussion .author')).map(el => el.innerText);
		const uniqueUsers = new Set(usersOnPage);

		// Add cached users to DOM first, since the calls for everyone else will be slow
		for (const user of uniqueUsers) {
			const cachedName = users[user];
			if (cachedName) {
				addUsersName(user, cachedName);
				uniqueUsers.delete(user);
			}
		}

		const userUrl = user => `https://github.com/${user}/following`;
		const requests = Array.from(uniqueUsers).map(username => {
			const req = fetch(userUrl(username));
			return req.then(res => res.text()).then(profile => ({
				username,
				profile
			}));
		});

		Promise.all(requests).then(profiles => {
			const userCache = {};

			for (const {username, profile} of profiles) {
				const profileDOM = new DOMParser().parseFromString(profile, 'text/html');
				const fullname = $(profileDOM).find('h1 strong').text().slice(1, -1);

				// Possible for a user to not set a name
				if (fullname) {
					userCache[username] = fullname;
					addUsersName(username, fullname);
				}
			}

			updateCachedUsers(Object.assign({}, users, userCache));
		}).catch(err => console.error(err));
	});
};
<commit_msg>Skip wrapper for console.error() (#444)<commit_after>window.showRealNames = () => {
	const storageKey = 'cachedNames';

	const getCachedUsers = cb => {
		chrome.storage.local.get(storageKey, data => cb(data[storageKey]));
	};

	const updateCachedUsers = users => {
		chrome.storage.local.set({[storageKey]: users});
	};

	const addUsersName = (user, name) => {
		const $usernameLinks = $(`.timeline-comment-header-text:not(.has-full-name) a[href="/${user}"]`);
		$usernameLinks.each((i, userLink) => {
			$(`<span class="comment-full-name">`).text(`${name} -`).insertAfter(userLink);
			$(userLink).closest('.timeline-comment-header-text').addClass('has-full-name');
		});
	};

	getCachedUsers((users = {}) => {
		const usersOnPage = Array.from($('.js-discussion .author')).map(el => el.innerText);
		const uniqueUsers = new Set(usersOnPage);

		// Add cached users to DOM first, since the calls for everyone else will be slow
		for (const user of uniqueUsers) {
			const cachedName = users[user];
			if (cachedName) {
				addUsersName(user, cachedName);
				uniqueUsers.delete(user);
			}
		}

		const userUrl = user => `https://github.com/${user}/following`;
		const requests = Array.from(uniqueUsers).map(username => {
			const req = fetch(userUrl(username));
			return req.then(res => res.text()).then(profile => ({
				username,
				profile
			}));
		});

		Promise.all(requests).then(profiles => {
			const userCache = {};

			for (const {username, profile} of profiles) {
				const profileDOM = new DOMParser().parseFromString(profile, 'text/html');
				const fullname = $(profileDOM).find('h1 strong').text().slice(1, -1);

				// Possible for a user to not set a name
				if (fullname) {
					userCache[username] = fullname;
					addUsersName(username, fullname);
				}
			}

			updateCachedUsers(Object.assign({}, users, userCache));
		}).catch(console.error);
	});
};
<|endoftext|>


<commit_before>/// <reference types="Cypress" />

describe('Setup', () => {
  it('Load the setup page', () => {
    cy.visit('/')
    cy.contains('You are about to install Wiki.js').should('exist')
  })
  it('Enter administrator email address', () => {
    cy.get('.v-input').contains('Administrator Email').next('input').click().type('test@example.com')
  })
  it('Enter a password', () => {
    cy.get('.v-input').contains('Password').next('input').click().type('12345678')
    cy.get('.v-input').contains('Confirm Password').next('input').click().type('12345678')
  })
  it('Enter a Site URL', () => {
    cy.get('.v-input').contains('Site URL').next('input').click().clear().type('http://localhost:3000')
  })
  it('Disable Telemetry', () => {
    cy.contains('Telemetry').next('.v-input').click()
  })
  it('Press Install', () => {
    cy.get('.v-card__actions').find('button').click()
  })
  it('Wait for install success', () => {
    cy.contains('Installation complete!', {timeout: 30000}).should('exist')
  })
  it('Redirect to login page', () => {
    cy.location('pathname', {timeout: 10000}).should('include', '/login')
  })
})
<commit_msg>fix: disable failing cypress test due to origin change<commit_after>/// <reference types="Cypress" />

describe('Setup', () => {
  it('Load the setup page', () => {
    cy.visit('/')
    cy.contains('You are about to install Wiki.js').should('exist')
  })
  it('Enter administrator email address', () => {
    cy.get('.v-input').contains('Administrator Email').next('input').click().type('test@example.com')
  })
  it('Enter a password', () => {
    cy.get('.v-input').contains('Password').next('input').click().type('12345678')
    cy.get('.v-input').contains('Confirm Password').next('input').click().type('12345678')
  })
  it('Enter a Site URL', () => {
    cy.get('.v-input').contains('Site URL').next('input').click().clear().type('http://localhost:3000')
  })
  it('Disable Telemetry', () => {
    cy.contains('Telemetry').next('.v-input').click()
  })
  it('Press Install', () => {
    cy.get('.v-card__actions').find('button').click()
  })
  it('Wait for install success', () => {
    cy.contains('Installation complete!', {timeout: 30000}).should('exist')
  })
  // -> Disabled because of origin change errors during CI tests
  //
  // it('Redirect to login page', () => {
  //   cy.location('pathname', {timeout: 10000}).should('include', '/login')
  // })
})
<|endoftext|>


<commit_before>'use strict';

if ('serviceWorker' in navigator &&
    // See http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
    (window.location.protocol === 'https:' ||
     window.location.hostname === 'localhost' ||
     window.location.hostname.indexOf('127.') === 0)) {
  navigator.serviceWorker.register('service-worker.js', {
    scope: './'
  }).then(function(registration) {
    // Check to see if there's an updated version of service-worker.js with new files to cache:
    // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-registration-update-method
    if (typeof registration.update == 'function') {
      registration.update();
    }

    // updatefound is fired if service-worker.js changes.
    registration.onupdatefound = function() {
      // The updatefound event implies that registration.installing is set; see
      // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
      var installingWorker = registration.installing;

      installingWorker.onstatechange = function() {
        switch (installingWorker.state) {
          case 'installed':
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and the fresh content will
              // have been added to the cache.
              // It's the perfect time to display a "New content is available; please refresh."
              // message in the page's interface.
              console.log('New or updated content is available.');
            } else {
              // At this point, everything has been precached, but the service worker is not
              // controlling the page. The service worker will not take control until the next
              // reload or navigation to a page under the registered scope.
              // It's the perfect time to display a "Content is cached for offline use." message.
              console.log('Content is cached, and will be available for offline use the ' +
                          'next time the page is loaded.')
            }
          break;

          case 'redundant':
            throw 'The installing service worker became redundant.';
        }
      };
    };
  }).catch(function(e) {
    console.error('Error during service worker registration:', e);
  });
}
<commit_msg>Added a comment about service-worker.js location.<commit_after>'use strict';

if ('serviceWorker' in navigator &&
    // See http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
    (window.location.protocol === 'https:' ||
     window.location.hostname === 'localhost' ||
     window.location.hostname.indexOf('127.') === 0)) {
  // Your service-worker.js *must* be located at the top-level directory relative to your site.
  // It won't be able to control pages unless it's located at the same level or higher than them.
  // *Don't* register service worker file in, e.g., a scripts/ sub-directory!
  // See https://github.com/slightlyoff/ServiceWorker/issues/468
  navigator.serviceWorker.register('service-worker.js', {
    scope: './'
  }).then(function(registration) {
    // Check to see if there's an updated version of service-worker.js with new files to cache:
    // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-registration-update-method
    if (typeof registration.update == 'function') {
      registration.update();
    }

    // updatefound is fired if service-worker.js changes.
    registration.onupdatefound = function() {
      // The updatefound event implies that registration.installing is set; see
      // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
      var installingWorker = registration.installing;

      installingWorker.onstatechange = function() {
        switch (installingWorker.state) {
          case 'installed':
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and the fresh content will
              // have been added to the cache.
              // It's the perfect time to display a "New content is available; please refresh."
              // message in the page's interface.
              console.log('New or updated content is available.');
            } else {
              // At this point, everything has been precached, but the service worker is not
              // controlling the page. The service worker will not take control until the next
              // reload or navigation to a page under the registered scope.
              // It's the perfect time to display a "Content is cached for offline use." message.
              console.log('Content is cached, and will be available for offline use the ' +
                          'next time the page is loaded.')
            }
          break;

          case 'redundant':
            throw 'The installing service worker became redundant.';
        }
      };
    };
  }).catch(function(e) {
    console.error('Error during service worker registration:', e);
  });
}
<|endoftext|>


<commit_before>const React = require('react');
const Radium = require('radium');
const moment = require('moment');

const Icon = require('./Icon');

const StyleConstants = require('../constants/Style');

const DatePicker = React.createClass({
  propTypes: {
    closeOnDateSelect: React.PropTypes.bool,
    defaultDate: React.PropTypes.number,
    format: React.PropTypes.string,
    locale: React.PropTypes.string,
    minimumDate: React.PropTypes.number,
    onDateSelect: React.PropTypes.func,
    placeholderText: React.PropTypes.string,
    primaryColor: React.PropTypes.string,
    style: React.PropTypes.object
  },

  getDefaultProps () {
    return {
      closeOnDateSelect: false,
      format: 'MMM D, YYYY',
      locale: 'en',
      onDateSelect () {},
      placeholderText: 'Select A Date',
      primaryColor: StyleConstants.Colors.PRIMARY
    };
  },

  getInitialState () {
    return {
      currentDate: null,
      inputValue: moment.unix(this.props.defaultDate).format(this.props.format),
      selectedDate: this.props.defaultDate,
      showCalendar: false
    };
  },

  _getSelectedDate () {
    const selectedDate = this.state.selectedDate;

    return selectedDate && moment.unix(selectedDate).isValid() ? this.state.selectedDate : moment().unix();
  },

  _handleDateSelect (date) {
    if (this.props.closeOnDateSelect) {
      this._handleScrimClick();
    }

    this.setState({
      inputValue: moment.unix(date).format(this.props.format),
      isValid: true,
      selectedDate: date
    });

    this.props.onDateSelect(date);
  },

  _handlePreviousClick () {
    const selectedDate = moment.unix(this._getSelectedDate()).locale(this.props.locale);
    let currentDate = this.state.currentDate ? this.state.currentDate.locale(this.props.locale) : selectedDate;

    currentDate = moment(currentDate.startOf('month').subtract(1, 'm'), this.props.format);

    this.setState({
      currentDate
    });
  },

  _handleNextClick () {
    const selectedDate = moment.unix(this._getSelectedDate()).locale(this.props.locale);
    let currentDate = this.state.currentDate ? this.state.currentDate.locale(this.props.locale) : selectedDate;

    currentDate = moment(currentDate.endOf('month').add(1, 'd'), this.props.format);

    this.setState({
      currentDate
    });
  },

  _handleScrimClick () {
    this.setState({
      showCalendar: false
    });
  },

  _toggleCalendar () {
    this.setState({
      showCalendar: !this.state.showCalendar
    });
  },

  _renderDayTitles (styles) {
    const days = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];

    return (

        {days.map((day, i) => {
          return (

                {day}

          );
        })}

    );
  },

  _renderMonthTable (currentDate, selectedDate, styles) {
    const days = [];
    const startDate = moment(currentDate, this.props.format).startOf('month').startOf('week');
    const endDate = moment(currentDate, this.props.format).endOf('month').endOf('week');
    const minimumDate = this.props.minimumDate ? moment.unix(this.props.minimumDate) : null;

    while (startDate.isBefore(endDate)) {
      const isCurrentMonth = startDate.month() === currentDate.month();
      const isCurrentDay = startDate.format(this.props.format) === selectedDate.format(this.props.format);
      const isToday = startDate.format(this.props.format) === moment().format(this.props.format);
      const noSelectDay = startDate.isBefore(minimumDate);
      const day = (

{startDate.date()}

      );

      days.push(day);
      startDate.add(1, 'd');
    }

    return days;
  },

  _renderSelectedDate (styles) {
    return (

        <Icon
          style={styles.calendarIcon}
          type='calendar'
        />

          {this.state.inputValue || this.props.placeholderText}

          <Icon
            style={styles.caret}
            type={this.state.showCalendar ? 'caret-up' : 'caret-down'}
          />

    );
  },

  render () {
    const styles = this.styles();
    const selectedDate = moment.unix(this._getSelectedDate()).locale(this.props.locale);
    const currentDate = this.state.currentDate ? this.state.currentDate.locale(this.props.locale) : selectedDate;

    return (

          {this._renderSelectedDate(styles)}

              <Icon
                onClick={this._handlePreviousClick}
                size={20}
                style={styles.navLeft}
                type='caret-left'
              />

              {currentDate.format('MMMM YYYY')}

              <Icon
                onClick={this._handleNextClick}
                size={20}
                style={styles.navRight}
                type='caret-right'
              />

            {this._renderDayTitles(styles)}
            {this._renderMonthTable(currentDate, selectedDate, styles)}

        {(this.state.showCalendar) ? (

        ) : null }

    );
  },

  styles () {
    return {
      calendarDay: {
        color: StyleConstants.Colors.FOG,
        float: 'left',
        height: 30,
        marginBottom: 2,
        position: 'relative',
        width: 35
      },
      calendarDayContent: {
        borderRadius: 3,
        height: 30,
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%) translateX(-50%)',
        width: 35,

        ':hover': {
          border: '1px solid' + this.props.primaryColor,
          borderRadius: 3,
          cursor: 'pointer'
        }
      },
      calendarDayDisabled: {
        ':hover': {
          background: 'none',
          color: StyleConstants.Colors.FOG
        }
      },
      calendarDayText: {
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%) translateX(-50%)'
      },
      calendarHeader: {
        clear: 'both',
        color: StyleConstants.Colors.CHARCOAL,
        fontSize: StyleConstants.FontSizes.LARGE,
        height: 30,
        marginBottom: 15,
        position: 'relative',
        textAlign: 'center'
      },
      calendarIcon: {
        left: 10,
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%)'
      },
      calendarWeekContent: {
        color: StyleConstants.Colors.ASH,
        float: 'left',
        fontFamily: StyleConstants.Fonts.SEMIBOLD,
        fontSize: StyleConstants.FontSizes.SMALL,
        height: 30,
        marginBottom: 2,
        position: 'relative',
        width: 35
      },
      calendarWeekText: {
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%) translateX(-50%)'
      },
      calendarWrapper: {
        backgroundColor: StyleConstants.Colors.WHITE,
        border: '1px solid ' + StyleConstants.Colors.FOG,
        borderRadius: 3,
        boxShadow: StyleConstants.ShadowHigh,
        boxSizing: 'border-box',
        display: this.state.showCalendar ? 'block' : 'none',
        marginTop: 10,
        padding: '20px 20px 0 20px',
        position: 'absolute',
        right: 0,
        width: 287,
        zIndex: 10
      },
      caret: {
        fill: this.state.showCalendar ? this.props.primaryColor : StyleConstants.Colors.ASH,
        position: 'absolute',
        right: 5,
        top: '50%',
        transform: 'translateY(-50%)'
      },
      caretWrapper: {
        position: 'absolute',
        top: '50%',
        right: 5
      },
      clearFix: {
        clear: 'both',
        marginBottom: 15
      },
      component: Object.assign({
        backgroundColor: StyleConstants.Colors.WHITE,
        clear: 'both',
        color: StyleConstants.Colors.BLACK,
        display: 'inline-block',
        float: 'right',
        fontFamily: StyleConstants.FontFamily,
        fontSize: StyleConstants.FontSizes.MEDIUM,
        marginBottom: 15,
        position: 'relative',
        WebkitAppearance: 'none',
        width: '100%',

        ':focus': {
          boxShadow: 'none',
          outline: 'none'
        }
      }, this.props.style),
      currentDay: {
        backgroundColor: StyleConstants.Colors.FOG
      },
      currentMonth: {
        color: StyleConstants.Colors.CHARCOAL
      },
      dateDisplay: {
        backgroundColor: 'transparent',
        border: 'none',
        cursor: 'pointer',
        fill: this.props.primaryColor,
        fontSize: StyleConstants.FontSizes.MEDIUM,
        outline: 'none',
        WebkitAppearance: 'none',
        zIndex: 2,

        ':focus': {
          border: 'none',
          boxShadow: 'none',
          outline: 'none'
        }
      },
      inputValue: {
        color: this.state.inputValue ? StyleConstants.Colors.CHARCOAL : StyleConstants.Colors.ASH,
        display: 'inline-block',
        left: 40,
        position: 'absolute'
      },
      month: {
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translate(-50%, -50%)'
      },
      navLeft: {
        position: 'absolute',
        left: 7,
        top: '50%',
        transform: 'translateY(-50%)'
      },
      navRight: {
        position: 'absolute',
        right: 7,
        top: '50%',
        transform: 'translateY(-50%)'
      },
      navWrapper: {
        borderColor: 'transparent',
        borderRadius: 2,
        borderStyle: 'solid',
        borderWidth: 1,
        cursor: 'pointer',
        height: 30,

        width: 35,

        ':hover': {
          borderColor: StyleConstants.Colors.FOG
        }
      },
      selectedDay: {
        backgroundColor: this.props.primaryColor,
        color: StyleConstants.Colors.WHITE
      },
      selectedDateWrapper: {
        borderColor: this.state.showCalendar ? this.props.primaryColor : StyleConstants.Colors.FOG,
        borderRadius: 3,
        borderStyle: 'solid',
        borderWidth: 1,
        cursor: 'pointer',
        height: 12,
        position: 'relative',
        padding: 13
      },
      scrim: {
        position: 'fixed',
        zIndex: 9,
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      }
    };
  }
});

module.exports = Radium(DatePicker);
<commit_msg>finishes up style changes to date picker<commit_after>const React = require('react');
const Radium = require('radium');
const moment = require('moment');

const Icon = require('./Icon');

const StyleConstants = require('../constants/Style');

const DatePicker = React.createClass({
  propTypes: {
    closeOnDateSelect: React.PropTypes.bool,
    defaultDate: React.PropTypes.number,
    format: React.PropTypes.string,
    locale: React.PropTypes.string,
    minimumDate: React.PropTypes.number,
    onDateSelect: React.PropTypes.func,
    placeholderText: React.PropTypes.string,
    primaryColor: React.PropTypes.string,
    style: React.PropTypes.object
  },

  getDefaultProps () {
    return {
      closeOnDateSelect: false,
      format: 'MMM D, YYYY',
      locale: 'en',
      onDateSelect () {},
      placeholderText: 'Select A Date',
      primaryColor: StyleConstants.Colors.PRIMARY
    };
  },

  getInitialState () {
    return {
      currentDate: null,
      inputValue: moment.unix(this.props.defaultDate).format(this.props.format),
      selectedDate: this.props.defaultDate,
      showCalendar: false
    };
  },

  _getSelectedDate () {
    const selectedDate = this.state.selectedDate;

    return selectedDate && moment.unix(selectedDate).isValid() ? this.state.selectedDate : moment().unix();
  },

  _handleDateSelect (date) {
    if (this.props.closeOnDateSelect) {
      this._handleScrimClick();
    }

    this.setState({
      inputValue: moment.unix(date).format(this.props.format),
      isValid: true,
      selectedDate: date
    });

    this.props.onDateSelect(date);
  },

  _handlePreviousClick () {
    const selectedDate = moment.unix(this._getSelectedDate()).locale(this.props.locale);
    let currentDate = this.state.currentDate ? this.state.currentDate.locale(this.props.locale) : selectedDate;

    currentDate = moment(currentDate.startOf('month').subtract(1, 'm'), this.props.format);

    this.setState({
      currentDate
    });
  },

  _handleNextClick () {
    const selectedDate = moment.unix(this._getSelectedDate()).locale(this.props.locale);
    let currentDate = this.state.currentDate ? this.state.currentDate.locale(this.props.locale) : selectedDate;

    currentDate = moment(currentDate.endOf('month').add(1, 'd'), this.props.format);

    this.setState({
      currentDate
    });
  },

  _handleScrimClick () {
    this.setState({
      showCalendar: false
    });
  },

  _toggleCalendar () {
    this.setState({
      showCalendar: !this.state.showCalendar
    });
  },

  _renderDayTitles (styles) {
    const days = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];

    return (

        {days.map((day, i) => {
          return (

                {day}

          );
        })}

    );
  },

  _renderMonthTable (currentDate, selectedDate, styles) {
    const days = [];
    const startDate = moment(currentDate, this.props.format).startOf('month').startOf('week');
    const endDate = moment(currentDate, this.props.format).endOf('month').endOf('week');
    const minimumDate = this.props.minimumDate ? moment.unix(this.props.minimumDate) : null;

    while (startDate.isBefore(endDate)) {
      const isCurrentMonth = startDate.month() === currentDate.month();
      const isCurrentDay = startDate.format(this.props.format) === selectedDate.format(this.props.format);
      const isToday = startDate.format(this.props.format) === moment().format(this.props.format);
      const noSelectDay = startDate.isBefore(minimumDate);
      const day = (

{startDate.date()}

      );

      days.push(day);
      startDate.add(1, 'd');
    }

    return days;
  },

  _renderSelectedDate (styles) {
    return (

        <Icon
          size={20}
          style={styles.calendarIcon}
          type='calendar'
        />

          {this.state.inputValue || this.props.placeholderText}

          <Icon
            size={20}
            style={styles.caret}
            type={this.state.showCalendar ? 'caret-up' : 'caret-down'}
          />

    );
  },

  render () {
    const styles = this.styles();
    const selectedDate = moment.unix(this._getSelectedDate()).locale(this.props.locale);
    const currentDate = this.state.currentDate ? this.state.currentDate.locale(this.props.locale) : selectedDate;

    return (

          {this._renderSelectedDate(styles)}

              <Icon
                onClick={this._handlePreviousClick}
                size={20}
                style={styles.navLeft}
                type='caret-left'
              />

              {currentDate.format('MMMM YYYY')}

              <Icon
                onClick={this._handleNextClick}
                size={20}
                style={styles.navRight}
                type='caret-right'
              />

            {this._renderDayTitles(styles)}
            {this._renderMonthTable(currentDate, selectedDate, styles)}

        {(this.state.showCalendar) ? (

        ) : null }

    );
  },

  styles () {
    return {
      calendarDay: {
        color: StyleConstants.Colors.FOG,
        float: 'left',
        height: 30,
        marginBottom: 2,
        position: 'relative',
        width: 35
      },
      calendarDayContent: {
        borderRadius: 3,
        height: 30,
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%) translateX(-50%)',
        width: 35,

        ':hover': {
          border: '1px solid' + this.props.primaryColor,
          borderRadius: 3,
          cursor: 'pointer'
        }
      },
      calendarDayDisabled: {
        ':hover': {
          background: 'none',
          color: StyleConstants.Colors.FOG
        }
      },
      calendarDayText: {
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%) translateX(-50%)'
      },
      calendarHeader: {
        clear: 'both',
        color: StyleConstants.Colors.CHARCOAL,
        fontSize: StyleConstants.FontSizes.LARGE,
        height: 30,
        marginBottom: 15,
        position: 'relative',
        textAlign: 'center'
      },
      calendarIcon: {
        left: 10,
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%)'
      },
      calendarWeekContent: {
        color: StyleConstants.Colors.ASH,
        float: 'left',
        fontFamily: StyleConstants.Fonts.SEMIBOLD,
        fontSize: StyleConstants.FontSizes.SMALL,
        height: 30,
        marginBottom: 2,
        position: 'relative',
        width: 35
      },
      calendarWeekText: {
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%) translateX(-50%)'
      },
      calendarWrapper: {
        backgroundColor: StyleConstants.Colors.WHITE,
        border: '1px solid ' + StyleConstants.Colors.FOG,
        borderRadius: 3,
        boxShadow: StyleConstants.ShadowHigh,
        boxSizing: 'border-box',
        display: this.state.showCalendar ? 'block' : 'none',
        marginTop: 10,
        padding: '20px 20px 0 20px',
        position: 'absolute',
        right: 0,
        width: 287,
        zIndex: 10
      },
      caret: {
        fill: this.state.showCalendar ? this.props.primaryColor : StyleConstants.Colors.ASH,
        position: 'absolute',
        right: 5,
        top: '50%',
        transform: 'translateY(-50%)'
      },
      caretWrapper: {
        position: 'absolute',
        top: '50%',
        right: 5
      },
      clearFix: {
        clear: 'both',
        marginBottom: 15
      },
      component: Object.assign({
        backgroundColor: StyleConstants.Colors.WHITE,
        clear: 'both',
        color: StyleConstants.Colors.BLACK,
        display: 'inline-block',
        float: 'right',
        fontFamily: StyleConstants.FontFamily,
        fontSize: StyleConstants.FontSizes.MEDIUM,
        marginBottom: 15,
        position: 'relative',
        WebkitAppearance: 'none',
        width: '100%',

        ':focus': {
          boxShadow: 'none',
          outline: 'none'
        }
      }, this.props.style),
      currentDay: {
        backgroundColor: StyleConstants.Colors.FOG
      },
      currentMonth: {
        color: StyleConstants.Colors.CHARCOAL
      },
      dateDisplay: {
        backgroundColor: 'transparent',
        border: 'none',
        cursor: 'pointer',
        fill: this.props.primaryColor,
        fontSize: StyleConstants.FontSizes.MEDIUM,
        outline: 'none',
        WebkitAppearance: 'none',
        zIndex: 2,

        ':focus': {
          border: 'none',
          boxShadow: 'none',
          outline: 'none'
        }
      },
      inputValue: {
        color: this.state.inputValue ? StyleConstants.Colors.CHARCOAL : StyleConstants.Colors.ASH,
        display: 'inline-block',
        left: 40,
        position: 'absolute',
        top: '50%',
        transform: 'translateY(-50%)'
      },
      month: {
        left: '50%',
        position: 'absolute',
        top: '50%',
        transform: 'translate(-50%, -50%)'
      },
      navLeft: {
        position: 'absolute',
        left: 7,
        top: '50%',
        transform: 'translateY(-50%)'
      },
      navRight: {
        position: 'absolute',
        right: 7,
        top: '50%',
        transform: 'translateY(-50%)'
      },
      navWrapper: {
        borderColor: 'transparent',
        borderRadius: 2,
        borderStyle: 'solid',
        borderWidth: 1,
        cursor: 'pointer',
        height: 30,

        width: 35,

        ':hover': {
          borderColor: StyleConstants.Colors.FOG
        }
      },
      selectedDay: {
        backgroundColor: this.props.primaryColor,
        color: StyleConstants.Colors.WHITE
      },
      selectedDateWrapper: {
        borderColor: this.state.showCalendar ? this.props.primaryColor : StyleConstants.Colors.FOG,
        borderRadius: 3,
        borderStyle: 'solid',
        borderWidth: 1,
        cursor: 'pointer',
        height: 13,
        position: 'relative',
        padding: '11px 10px 12px'
      },
      scrim: {
        position: 'fixed',
        zIndex: 9,
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      }
    };
  }
});

module.exports = Radium(DatePicker);
<|endoftext|>


<commit_before>import React from 'react'
import { debounce } from 'lodash'
import cx from 'classnames'
const { func, node, string } = React.PropTypes

var loaders

const register = (loader, onShow) => {
  loaders.push({loader, onShow})
  checkOne(loader, onShow)
}

const unregister = (loader) => {
  if (!loaders) return // this happens during hot reloading
  loaders = loaders.filter(x => x.loader !== loader)
}

const checkOne = (loader, onShow) => {
  const height = document.documentElement.clientHeight
  const { top, bottom } = loader.getBoundingClientRect()
  if ((top > 0 && top < height) || (bottom > 0 && bottom < height)) {
    onShow()
    unregister(loader)
  }
}

const check = debounce(() => {
  const height = document.documentElement.clientHeight
  loaders.forEach(({ loader, onShow }) => {
    const { top, bottom } = loader.getBoundingClientRect()
    if ((top > 0 && top < height) || (bottom > 0 && bottom < height)) {
      onShow()
      unregister(loader)
    }
  })
}, 200)

export const init = () => {
  loaders = []
  window.addEventListener('resize', check)
  window.addEventListener('scroll', check)
}

export default class LazyLoader extends React.Component {
  static propTypes = {
    children: node,
    onContentVisible: func,
    className: string
  }

  constructor (props) {
    super(props)
    this.state = {}
  }

  componentDidMount () {
    const { onContentVisible } = this.props
    register(this.refs.loader, () => {
      if (onContentVisible) onContentVisible()
      this.setState({visible: true})
    })
  }

  componentWillUnmount () {
    unregister(this.refs.loader)
  }

  render () {
    const { className, children } = this.props
    return

      {this.state.visible && children}

  }
}
<commit_msg>make lazy-loading happen a bit earlier<commit_after>import React from 'react'
import { debounce } from 'lodash'
import cx from 'classnames'
const { func, node, string } = React.PropTypes

const padding = 50
var loaders

const register = (loader, onShow) => {
  loaders.push({loader, onShow})
  checkOne(loader, onShow)
}

const unregister = (loader) => {
  if (!loaders) return // this happens during hot reloading
  loaders = loaders.filter(x => x.loader !== loader)
}

const checkOne = (loader, onShow) => {
  const height = document.documentElement.clientHeight
  const { top, bottom } = loader.getBoundingClientRect()
  if ((top > -padding && top < height + padding) ||
    (bottom > -padding && bottom < height + padding)) {
    onShow()
    unregister(loader)
  }
}

const check = debounce(() => {
  const height = document.documentElement.clientHeight
  loaders.forEach(({ loader, onShow }) => {
    const { top, bottom } = loader.getBoundingClientRect()
    if ((top > 0 && top < height) || (bottom > 0 && bottom < height)) {
      onShow()
      unregister(loader)
    }
  })
}, 100)

export const init = () => {
  loaders = []
  window.addEventListener('resize', check)
  window.addEventListener('scroll', check)
}

export default class LazyLoader extends React.Component {
  static propTypes = {
    children: node,
    onContentVisible: func,
    className: string
  }

  constructor (props) {
    super(props)
    this.state = {}
  }

  componentDidMount () {
    const { onContentVisible } = this.props
    register(this.refs.loader, () => {
      if (onContentVisible) onContentVisible()
      this.setState({visible: true})
    })
  }

  componentWillUnmount () {
    unregister(this.refs.loader)
  }

  render () {
    const { className, children } = this.props
    return

      {this.state.visible && children}

  }
}
<|endoftext|>


<commit_before>// dependencies
//
import React from 'react';
import uniqueId from 'lodash/uniqueId';

const CheckboxGroup = ({ label, options }) => (

        {label
            ? {label}
            : null
        }

);

export default CheckboxGroup;
<commit_msg>updated component label class<commit_after>// dependencies
//
import React from 'react';
import uniqueId from 'lodash/uniqueId';

const CheckboxGroup = ({ label, options }) => (

        {label
            ? {label}
            : null
        }

);

export default CheckboxGroup;
<|endoftext|>


<commit_before>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

import React from 'react';
import { shallow, render } from 'enzyme';
import chai from 'chai';
import Panel from './Panel';

const expect = chai.expect;

/* *****************************************************************************
the Panel component
    should contain a div.panel-heading if header is set
    should not contain a div.panel-heading if header is not set
    should contain a div.panel-body
    should include child elements inside the div.panel-body

    should be a div.panel.panel-default when style is not set
    should be a div.panel.panel-danger when style="danger"
    should be a div.panel.panel-danger when style="error"
    should be a div.panel.panel-warning when style="warning"
    should be a div.panel.panel-warning when style="warn"
    should be a div.panel.panel-info when style="info"
    should be a div.panel.panel-success when style="success"
    should be a div.panel.panel-success when style="ok"
    should be a div.panel.panel-primary when style="primary"
*/
describe('the Panel component', () => {

    it('should be a div.panel.panel-default', () => {
        const component = shallow(<Panel />);

        expect(component.is('div.panel.panel-default')).to.equal(true);
    });

    it('should contain a div.panel-heading if header is set', () => {
        const header = 'this is a header';

        const component = render(<Panel header={header} />);

        expect(component.find('div.panel div.panel-heading').length)
            .to.equal(1, 'div.panel-heading');
        expect(component.find('div.panel div.panel-heading h3.panel-title').length)
            .to.equal(1, 'h3.panel-title');
        expect(component.find('div.panel div.panel-heading h3.panel-title').text())
            .to.equal(header, 'h3.panel-title text');
    });

    it('should not contain a div.panel-heading if header is not set', () => {
        const component = render(<Panel />);

        expect(component.find('div.panel div.panel-heading').length)
            .to.equal(0, 'div.panel-heading');
        expect(component.find('div.panel div.panel-heading h3.panel-title').length)
            .to.equal(0, 'h3.panel-title');
    });

    it('should contain a div.panel-body', () => {
        const component = render(<Panel />);

        expect(component.find('div.panel div.panel-body').length).to.equal(1, 'div.panel-body');
    });

    it('should include child elements inside the div.panel-body', () => {
        const children = [

,

,

,
        ];

        const component = render(<Panel>{children}</Panel>);

        expect(component.find('div.panel div.panel-body div.child-1').length).to.equal(1);
        expect(component.find('div.panel div.panel-body div.child-2').length).to.equal(1);
        expect(component.find('div.panel div.panel-body div.child-3').length).to.equal(1);
    });

});
<commit_msg>renamed/moved panel-default case<commit_after>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

import React from 'react';
import { shallow, render } from 'enzyme';
import chai from 'chai';
import Panel from './Panel';

const expect = chai.expect;

/* *****************************************************************************
the Panel component
    should contain a div.panel-heading if header is set
    should not contain a div.panel-heading if header is not set
    should contain a div.panel-body
    should include child elements inside the div.panel-body

    should be a div.panel.panel-default when style is not set
    should be a div.panel.panel-danger when style="danger"
    should be a div.panel.panel-danger when style="error"
    should be a div.panel.panel-warning when style="warning"
    should be a div.panel.panel-warning when style="warn"
    should be a div.panel.panel-info when style="info"
    should be a div.panel.panel-success when style="success"
    should be a div.panel.panel-success when style="ok"
    should be a div.panel.panel-primary when style="primary"
*/
describe('the Panel component', () => {

    it('should contain a div.panel-heading if header is set', () => {
        const header = 'this is a header';

        const component = render(<Panel header={header} />);

        expect(component.find('div.panel div.panel-heading').length)
            .to.equal(1, 'div.panel-heading');
        expect(component.find('div.panel div.panel-heading h3.panel-title').length)
            .to.equal(1, 'h3.panel-title');
        expect(component.find('div.panel div.panel-heading h3.panel-title').text())
            .to.equal(header, 'h3.panel-title text');
    });

    it('should not contain a div.panel-heading if header is not set', () => {
        const component = render(<Panel />);

        expect(component.find('div.panel div.panel-heading').length)
            .to.equal(0, 'div.panel-heading');
        expect(component.find('div.panel div.panel-heading h3.panel-title').length)
            .to.equal(0, 'h3.panel-title');
    });

    it('should contain a div.panel-body', () => {
        const component = render(<Panel />);

        expect(component.find('div.panel div.panel-body').length).to.equal(1, 'div.panel-body');
    });

    it('should include child elements inside the div.panel-body', () => {
        const children = [

,

,

,
        ];

        const component = render(<Panel>{children}</Panel>);

        expect(component.find('div.panel div.panel-body div.child-1').length).to.equal(1);
        expect(component.find('div.panel div.panel-body div.child-2').length).to.equal(1);
        expect(component.find('div.panel div.panel-body div.child-3').length).to.equal(1);
    });

    it('should be a div.panel.panel-default when style is not set', () => {
        const component = shallow(<Panel />);

        expect(component.is('div.panel.panel-default')).to.equal(true);
    });

});
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
    Thing to get history of DAO transactions
*/

var Web3 = require("web3");
var web3;

require( '../../db-dao.js' );
var mongoose = require( 'mongoose' );
var DAOCreatedToken = mongoose.model('DAOCreatedToken');
var DAOTransferToken = mongoose.model('DAOTransferToken');

if (typeof web3 !== "undefined") {
  web3 = new Web3(web3.currentProvider);
} else {
  web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

if (web3.isConnected())
  console.log("Web3 connection established");
else
  throw "No connection";

var daoABI = [{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"FuelingToDate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"CreatedToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"newCurator","type":"bool"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":true,"name":"voter","type":"address"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"bool"},{"indexed":false,"name":"quorum","type":"uint256"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_newCurator","type":"address"}],"name":"NewCurator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_recipient","type":"address"},{"indexed":false,"name":"_allowed","type":"bool"}],"name":"AllowedRecipientChanged","type":"event"}];
var daoContract = web3.eth.contract(daoABI);
var DAO = daoContract.at("0xbb9bc244d798123fde783fcc1c72d3bb8c189413");

var creationBlock = 1428757;
var creationEnd = 1599205;

var populateCreatedTokens = function () {
  //total: 58713

  var event = DAO.CreatedToken;
  event({}, {fromBlock: creationBlock, toBlock: creationEnd}).get( function(err, log) {
      if (err) {
        console.error(err);
        process.exit(9);
      } else {
        for (var l in log) {
          try {
            var newToken = {
                "transactionHash": log[l].transactionHash,
                "blockNumber": log[l].blockNumber,
                "amount": log[l].args.amount,
                "to": log[l].args.to
            }
          } catch (e) {
            console.error(e);
            continue;
          }

          new DAOCreatedToken(newToken).save( function( err, token, count ){
            if ( typeof err !== 'undefined' && err ) {
              if (err.code == 11000) {
                  console.log('Skip: Duplicate tx ' +
                  log[l].transactionHash + ': ' +
                  err);
              } else {
                 console.log('Error: Aborted due to error on ' +
                      'block number ' + log[l].blockNumber.toString() + ': ' +
                      err);
                 process.exit(9);
              }
            } else
              console.log('DB successfully written for tx ' +
                        token.transactionHash );

          });
        }
      }

  });
}

var populateTransferTokens = function () {

  var event = DAO.Transfer;
  event({}, {fromBlock: creationEnd, toBlock: "latest"}).get( function(err, log) {
      if (err) {
        console.error(err);
        process.exit(9);
      } else {
        for (var l in log) {
          try {
            var newToken = {
                "transactionHash": log[l].transactionHash,
                "blockNumber": log[l].blockNumber,
                "amount": log[l].args._amount,
                "to": log[l].args._to,
                "from": log[l].args._from
            }
          } catch (e) {
            console.error(e);
            continue;
          }
          try {
            var block = web3.eth.getBlock(log[l].blockNumber);
            newToken.timestamp = block.timestamp;
          } catch (e) {
            console.error(e);
            continue;
          }
          new DAOTransferToken(newToken).save( function( err, token, count ){
            if ( typeof err !== 'undefined' && err ) {
              if (err.code == 11000) {
                  console.log('Skip: Duplicate tx ' +
                  log[l].transactionHash + ': ' +
                  err);
                  return null;
              } else {
                 console.log('Error: Aborted due to error on ' +
                      'block number ' + log[l].blockNumber.toString() + ': ' +
                      err);
                 process.exit(9);
              }
            } else
              console.log('DB successfully written for tx ' +
                        log[l].transactionHash );

          });
        }
      }

  });
}

var bulkTimeUpdate = function(bulk, callback) {
  console.log("Bulk execution started");
  bulk.execute(function(err,result) {
    if (err)
      console.error(err);
    else
      console.log(result.toJSON());
    callback();
  });
}

var async = require('async');

var patchTimestamps = function(collection) {
  mongoose.connection.on("open", function(err,conn) {

    var bulk = collection.initializeOrderedBulkOp();

    var bulkOps = [];
    var count = 0;
    var missingCount;
    collection.count({timestamp: null}, function(err, c) {
      missingCount = c;
      console.log("Missing: " + JSON.stringify(missingCount));
    });

    collection.find({timestamp: null}).limit(1000).forEach(function(doc) {

      try {
        var block = web3.eth.getBlock(doc.blockNumber);
      } catch (e) {
        console.error(e); return;
      }

      bulk.find({ '_id': doc._id }).updateOne({
          '$set': { 'timestamp': block.timestamp }
      });
      count++;
      if(count % 100 === 0) {
        // Execute per 1000 operations and re-init
        bulkOps.push(bulk);
        console.log(count);
        bulk = collection.initializeOrderedBulkOp();
      }
      if(count == missingCount || count === 1000) {
        // Clean up queues
        console.log(count);
        bulkOps.push(bulk);

        async.forEach(bulkOps, function(bulkOp, callback) {
          bulkTimeUpdate(bulkOp, callback);
        }, function(err) {
          if (err) { console.log(err); return; }
        });
      }
    });

  })
}

mongoose.connect( 'mongodb://localhost/blockDB' );
mongoose.set('debug', true);

/patchTimestamps(DAOCreatedToken.collection)
// populateCreatedTokens();
// populateTransferTokens();<commit_msg>fix comment<commit_after>#!/usr/bin/env node

/*
    Thing to get history of DAO transactions
*/

var Web3 = require("web3");
var web3;

require( '../../db-dao.js' );
var mongoose = require( 'mongoose' );
var DAOCreatedToken = mongoose.model('DAOCreatedToken');
var DAOTransferToken = mongoose.model('DAOTransferToken');

if (typeof web3 !== "undefined") {
  web3 = new Web3(web3.currentProvider);
} else {
  web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

if (web3.isConnected())
  console.log("Web3 connection established");
else
  throw "No connection";

var daoABI = [{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"FuelingToDate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"CreatedToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"newCurator","type":"bool"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":true,"name":"voter","type":"address"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"bool"},{"indexed":false,"name":"quorum","type":"uint256"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_newCurator","type":"address"}],"name":"NewCurator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_recipient","type":"address"},{"indexed":false,"name":"_allowed","type":"bool"}],"name":"AllowedRecipientChanged","type":"event"}];
var daoContract = web3.eth.contract(daoABI);
var DAO = daoContract.at("0xbb9bc244d798123fde783fcc1c72d3bb8c189413");

var creationBlock = 1428757;
var creationEnd = 1599205;

var populateCreatedTokens = function () {
  //total: 58713

  var event = DAO.CreatedToken;
  event({}, {fromBlock: creationBlock, toBlock: creationEnd}).get( function(err, log) {
      if (err) {
        console.error(err);
        process.exit(9);
      } else {
        for (var l in log) {
          try {
            var newToken = {
                "transactionHash": log[l].transactionHash,
                "blockNumber": log[l].blockNumber,
                "amount": log[l].args.amount,
                "to": log[l].args.to
            }
          } catch (e) {
            console.error(e);
            continue;
          }

          new DAOCreatedToken(newToken).save( function( err, token, count ){
            if ( typeof err !== 'undefined' && err ) {
              if (err.code == 11000) {
                  console.log('Skip: Duplicate tx ' +
                  log[l].transactionHash + ': ' +
                  err);
              } else {
                 console.log('Error: Aborted due to error on ' +
                      'block number ' + log[l].blockNumber.toString() + ': ' +
                      err);
                 process.exit(9);
              }
            } else
              console.log('DB successfully written for tx ' +
                        token.transactionHash );

          });
        }
      }

  });
}

var populateTransferTokens = function () {

  var event = DAO.Transfer;
  event({}, {fromBlock: creationEnd, toBlock: "latest"}).get( function(err, log) {
      if (err) {
        console.error(err);
        process.exit(9);
      } else {
        for (var l in log) {
          try {
            var newToken = {
                "transactionHash": log[l].transactionHash,
                "blockNumber": log[l].blockNumber,
                "amount": log[l].args._amount,
                "to": log[l].args._to,
                "from": log[l].args._from
            }
          } catch (e) {
            console.error(e);
            continue;
          }
          try {
            var block = web3.eth.getBlock(log[l].blockNumber);
            newToken.timestamp = block.timestamp;
          } catch (e) {
            console.error(e);
            continue;
          }
          new DAOTransferToken(newToken).save( function( err, token, count ){
            if ( typeof err !== 'undefined' && err ) {
              if (err.code == 11000) {
                  console.log('Skip: Duplicate tx ' +
                  log[l].transactionHash + ': ' +
                  err);
                  return null;
              } else {
                 console.log('Error: Aborted due to error on ' +
                      'block number ' + log[l].blockNumber.toString() + ': ' +
                      err);
                 process.exit(9);
              }
            } else
              console.log('DB successfully written for tx ' +
                        log[l].transactionHash );

          });
        }
      }

  });
}

var bulkTimeUpdate = function(bulk, callback) {
  console.log("Bulk execution started");
  bulk.execute(function(err,result) {
    if (err)
      console.error(err);
    else
      console.log(result.toJSON());
    callback();
  });
}

var async = require('async');

var patchTimestamps = function(collection) {
  mongoose.connection.on("open", function(err,conn) {

    var bulk = collection.initializeOrderedBulkOp();

    var bulkOps = [];
    var count = 0;
    var missingCount;
    collection.count({timestamp: null}, function(err, c) {
      missingCount = c;
      console.log("Missing: " + JSON.stringify(missingCount));
    });

    collection.find({timestamp: null}).limit(1000).forEach(function(doc) {

      try {
        var block = web3.eth.getBlock(doc.blockNumber);
      } catch (e) {
        console.error(e); return;
      }

      bulk.find({ '_id': doc._id }).updateOne({
          '$set': { 'timestamp': block.timestamp }
      });
      count++;
      if(count % 100 === 0) {
        // Execute per 1000 operations and re-init
        bulkOps.push(bulk);
        console.log(count);
        bulk = collection.initializeOrderedBulkOp();
      }
      if(count == missingCount || count === 1000) {
        // Clean up queues
        console.log(count);
        bulkOps.push(bulk);

        async.forEach(bulkOps, function(bulkOp, callback) {
          bulkTimeUpdate(bulkOp, callback);
        }, function(err) {
          if (err) { console.log(err); return; }
        });
      }
    });

  })
}

mongoose.connect( 'mongodb://localhost/blockDB' );
mongoose.set('debug', true);

// patchTimestamps(DAOCreatedToken.collection)
// populateCreatedTokens();
populateTransferTokens();<|endoftext|>


<commit_before>import Terrain from 'react-icons/lib/md/terrain'
import SnowFlake from 'react-icons/lib/ti/weather-snow'
import Calendar from 'react-icons/lib/fa/calendar'
import { SkiDayRow } from './SkiDayRow'

export const SkiDayList = ({days}) => (

				Date
				Resort
				Powder
				Backcountry

			{days.map((day, i) =>
				<SkiDayRow key={i}
                   resort={day.resort}
									 date={day.date}
									 powder={day.powder}
									 backcountry={day.backcountry}/>
				)}

)
<commit_msg>Refactor using the array spread operator.<commit_after>import Terrain from 'react-icons/lib/md/terrain'
import SnowFlake from 'react-icons/lib/ti/weather-snow'
import Calendar from 'react-icons/lib/fa/calendar'
import { SkiDayRow } from './SkiDayRow'

export const SkiDayList = ({days}) => (

				Date
				Resort
				Powder
				Backcountry

			{days.map((day, i) =>
				<SkiDayRow key={i}
                   {...day}/>
				)}

)
<|endoftext|>