<commit_before>import React, { Component } from 'react';
import globalStyles from '../assets/styles/globals.scss';
import localStyles from './styles/contact.scss';
import Popover from 'react-popover'

//assets
import skyline from '../assets/images/skyline.svg';
import tower from '../assets/images/tower.svg';
import team from '../assets/images/teamblue.png';

const styles = Object.assign({}, localStyles, globalStyles);
export default class Contact extends Component {

  constructor (props) {
    super(props)
    this.state = {
      email: '',
      subject: '',
      message: '',
      error: '',
      open: false,
      spinnerClass: 'fa-send',
      sent: false,
      sendText: 'Send'
    }
  }

  validateEmail () {
    const email = this.state.email
    if (!email) {
      this.setState({ open: true, error: 'Please enter your email address so that we can get back in touch with you!' })
    }
    else if (!/^.+@.+\..+$/.test(email)) {
      this.setState({ open: true, error: 'Please make sure your email address is valid. For example: hello@langa.io' })
    }
    else {
      return true
    }
  }

  validateMessage () {
    const message = this.state.message
    if (!message) {
      this.setState({ open: true, error: 'Please enter a message.' })
    }
    else if (message.length < 10) {
      this.setState({ open: true, error: 'Please provide in more detail so that we know how we can help.' })
    }
    else {
      return true
    }
  }

  closePopover () {
    this.setState({ open: false })
  }

  onSubjectChanged (e) {
    this.setState({ subject: e.target.value })
  }
  onEmailChanged (e) {
    this.setState({ email: e.target.value })
  }
  onMessageChanged (e) {
    this.setState({ message: e.target.value })
  }
  submitForm (e) {
    if (this.state.sent) {
      this.setState({ error: 'We received your message, and will get in touch shortly! Thanks.', open: true })
      return
    }

    if (!this.validateMessage()) return
    if (!this.validateEmail()) return

    this.setState({ spinnerClass: 'fa-spin fa-refresh' })

    const req = new XMLHttpRequest()
    //req.open('POST', '//api.langa.io/email', true)
    req.open('POST', '//localhost:4000/email', true)
    req.setRequestHeader('Content-Type', 'application/json')
    req.onreadystatechange = () => {
      if (req.readyState === 4 && req.status === 200) {
        const result = JSON.parse(req.responseText)
        if (result.status == 'sent') {
          this.setState({ sent: true, spinnerClass: 'fa-check', sendText: 'Sent!' })
        }
      }
      else {
        console.log(req.responseText)
        this.setState({ error: 'Oops! Our emailer is having some problems right now. Please email hello@langa.io' })
        this.setState({ sent: true, spinnerClass: 'fa-warn' })
      }
    }
    req.send(JSON.stringify({
      email: this.state.email,
      subject: this.state.subject,
      message: this.state.message
    }))
  }

  render () {
    const { sendText, spinnerClass, error, open } = this.state
      return (

Contact

              <div className={`${styles.flexMiddle}`}>
                  <div className={`${styles.contactTower}`}>
                      <form className={`${styles.form}`}>
                          <input placeholder="What's your Email Address?" onChange={e => this.onEmailChanged(e)}></input>
                          <input placeholder='Message Subject' onChange={e => this.onSubjectChanged(e)}></input>
                          <textarea
                            placeholder='How can we help?'
                            type="text"
                            onChange={e => this.onMessageChanged(e)}
                            className={`${styles.projectInfo}`}>
                          </textarea>
                          <button type="button" className={`${styles.submit}`} onClick={e => this.submitForm(e)}>

                            {sendText}
                          </button>
                          <Popover
                            offset={8}
                            enterExitTransitionDurationMs={200}
                            refreshIntervalMs={10000}
                            className={styles.Popoverbody}
                            isOpen={open}
                            body={

                                {error}

                            }
                            onOuterAction={() => this.closePopover()}
                            place='below'>

 this.closePopover()}>

                          </Popover>
                      </form>
                      <img src={tower} className={`${styles.tower}`}/>

                  <img src={team} className={`${styles.teamimage}`}/>

      );
  }
}
<commit_msg>fix email url<commit_after>import React, { Component } from 'react';
import globalStyles from '../assets/styles/globals.scss';
import localStyles from './styles/contact.scss';
import Popover from 'react-popover'

//assets
import skyline from '../assets/images/skyline.svg';
import tower from '../assets/images/tower.svg';
import team from '../assets/images/teamblue.png';

const styles = Object.assign({}, localStyles, globalStyles);
export default class Contact extends Component {

  constructor (props) {
    super(props)
    this.state = {
      email: '',
      subject: '',
      message: '',
      error: '',
      open: false,
      spinnerClass: 'fa-send',
      sent: false,
      sendText: 'Send'
    }
  }

  validateEmail () {
    const email = this.state.email
    if (!email) {
      this.setState({ open: true, error: 'Please enter your email address so that we can get back in touch with you!' })
    }
    else if (!/^.+@.+\..+$/.test(email)) {
      this.setState({ open: true, error: 'Please make sure your email address is valid. For example: hello@langa.io' })
    }
    else {
      return true
    }
  }

  validateMessage () {
    const message = this.state.message
    if (!message) {
      this.setState({ open: true, error: 'Please enter a message.' })
    }
    else if (message.length < 10) {
      this.setState({ open: true, error: 'Please provide in more detail so that we know how we can help.' })
    }
    else {
      return true
    }
  }

  closePopover () {
    this.setState({ open: false })
  }

  onSubjectChanged (e) {
    this.setState({ subject: e.target.value })
  }
  onEmailChanged (e) {
    this.setState({ email: e.target.value })
  }
  onMessageChanged (e) {
    this.setState({ message: e.target.value })
  }
  submitForm (e) {
    if (this.state.sent) {
      this.setState({ error: 'We received your message, and will get in touch shortly! Thanks.', open: true })
      return
    }

    if (!this.validateMessage()) return
    if (!this.validateEmail()) return

    this.setState({ spinnerClass: 'fa-spin fa-refresh' })

    const req = new XMLHttpRequest()
    req.open('POST', '//api.langa.io/email', true)
    req.setRequestHeader('Content-Type', 'application/json')
    req.onreadystatechange = () => {
      if (req.readyState === 4 && req.status === 200) {
        const result = JSON.parse(req.responseText)
        if (result.status == 'sent') {
          this.setState({ sent: true, spinnerClass: 'fa-check', sendText: 'Sent!' })
        }
      }
      else {
        console.log(req.responseText)
        this.setState({ error: 'Oops! Our emailer is having some problems right now. Please email hello@langa.io' })
        this.setState({ sent: true, spinnerClass: 'fa-warn' })
      }
    }
    req.send(JSON.stringify({
      email: this.state.email,
      subject: this.state.subject,
      message: this.state.message
    }))
  }

  render () {
    const { sendText, spinnerClass, error, open } = this.state
      return (

Contact

              <div className={`${styles.flexMiddle}`}>
                  <div className={`${styles.contactTower}`}>
                      <form className={`${styles.form}`}>
                          <input placeholder="What's your Email Address?" onChange={e => this.onEmailChanged(e)}></input>
                          <input placeholder='Message Subject' onChange={e => this.onSubjectChanged(e)}></input>
                          <textarea
                            placeholder='How can we help?'
                            type="text"
                            onChange={e => this.onMessageChanged(e)}
                            className={`${styles.projectInfo}`}>
                          </textarea>
                          <button type="button" className={`${styles.submit}`} onClick={e => this.submitForm(e)}>

                            {sendText}
                          </button>
                          <Popover
                            offset={8}
                            enterExitTransitionDurationMs={200}
                            refreshIntervalMs={10000}
                            className={styles.Popoverbody}
                            isOpen={open}
                            body={

                                {error}

                            }
                            onOuterAction={() => this.closePopover()}
                            place='below'>

 this.closePopover()}>

                          </Popover>
                      </form>
                      <img src={tower} className={`${styles.tower}`}/>

                  <img src={team} className={`${styles.teamimage}`}/>

      );
  }
}
<|endoftext|>


<commit_before>/* This was inspired by Zepto.js */
Wheel.View.subclass('Wheel.EventManager', {
  initializeDom: function(opts) {
    this.optionize(opts);
    this.$ = $(document);
  },

  listen: function() {
    var self = this;
    this.$.on('dragstart', function(e) {
      self.onDragStart(e);
    });
  },

  onDragStart: function(e) {
    e.preventDefault();
    this._setTarget(e);
    this.touch.type = 'drag';
  },

  onStart: function(e) {
    this.touch.ctrl = e.ctrlKey;
    e = this._normalizeEvent(e);
    this._setTarget(e);

    // clear existing timeout because there is a new touch start event
    // timeouts are set for 'taphold' and/or for 'tap'
    this._clearTimeout();

    // set up the touch object
    this.touch.x1 = e.pageX;
    this.touch.y1 = e.pageY;
    this.touch.time = Date.now();

    if (!this._testDoubleTap() && !this.touch.ctrl) {
      // wait to see if this is a taphold event
      var self = this;
      this.touchTimout = setTimeout(function() {
        self._testTouchhold(e);
      }, this.HOLD_DELAY);
    }
  },

  onMove: function(e) {
    this._handleDragMove(e);
    if (this.touch.type) { return; }
    e = this._unpackEvent(e);
    this._testSwipe(e);
  },

  onEnd: function(e) {
    this._handleDragEnd(e);
    if (this.touch.type || this.touch.ctrl) {
      this._resetTouch();
    } else {
      e = this._unpackEvent(e);
      this._handleTap(e);
    }
  },

  _triggerEvent: function(originalEvent, altType) {
    if (!this.target) { return; }
    var x = 'x2' in this.touch ? this.touch.x2 : this.touch.x1;
    var y = 'y2' in this.touch ? this.touch.y2 : this.touch.y1;

    this.target.trigger($.Event(altType || this.touch.type, {
      originalEvent: originalEvent,
      pageX: x,
      pageY: y
    }));
  },

  _setTarget: function(e) {
    var node = e.target;
    this.target = $('tagName' in node ? node : node.parentNode);
  },

  _clearTimeout: function() {
    this.touchTimeout && clearTimeout(this.touchTimeout);
  },

  _resetTouch: function() {
    this.lastTouch = this.touch;
    this.lastTouch.target = this.target;
    this.target = null;
    this.touch = {};
  },

  _normalizeEvent: function(e) {
    return e;
  },

  _unpackEvent: function(e) {
    e = this._normalizeEvent(e);

    if (e.type !== 'touchend') {
      this.touch.x2 = e.pageX >= 0 ? e.pageX : this.touch.x1;
      this.touch.y2 = e.pageY >= 0 ? e.pageY : this.touch.y1;
    }

    return e;
  },

  _handleTap: function(e) {
    if (this.RESPONSIVE_TAP) {
      this._onTap(e);
    } else {
      // wait to see if it is a tap, or if it is a double tap
      var self = this;
      this.touchTimeout = setTimeout(function(){
        self._onTap(e);
      }, this.DOUBLE_DELAY);
    }
  },

  _handleDragMove: function(e) {
    if (this.touch.type && this.touch.type === 'drag') {
      e.preventDefault();
      e = this._unpackEvent(e);
      this._triggerEvent(e, 'dragmove');
    }
  },

  _handleDragEnd: function(e) {
    if (this.touch.type && this.touch.type === 'drag') {
      e = this._unpackEvent(e);
      this._triggerEvent(e, 'dragend');
    }
  },

  _onTap: function(e) {
    this.touchTimeout = null;
    if ( this.target && e.target === this.target[0] &&
          this.TAP_TOLERANCE >= Math.abs(this.touch.x1 - this.touch.x2) &&
          this.TAP_TOLERANCE >= Math.abs(this.touch.y1 - this.touch.y2) ) {
      this.touch.type = 'tap';
      this._triggerEvent(e);
    }
    this._resetTouch();
  },

  _testDoubleTap: function(e) {
    // what about checking for distance from original tap
    var delta = this.lastTouch && this.lastTouch.time ? this.touch.time - this.lastTouch.time : 0;
    if (delta > 0 && delta <= this.DOUBLE_DELAY) {
      this.touch.type = 'doubletap';
      this._triggerEvent(e);
      return true;
    }
  },

  _testSwipe: function(e) {
    // if we have moved a certain distance, call it a swipe
    if (Math.abs(this.touch.x1 - this.touch.x2) >= this.SWIPE_TOLERANCE ||
        Math.abs(this.touch.y1 - this.touch.y2) >= this.SWIPE_TOLERANCE) {
      this.touch.type = 'swipe';
      var _direction = this._direction(this.touch.x1, this.touch.x2, this.touch.y1, this.touch.y2);
      if ( this.preventScroll(_direction) ) {
        e.preventDefault();
      }
      this._triggerEvent(e);
      this._triggerEvent(e, "swipe" + _direction);
    }
  },

  _testTouchhold: function(e) {
    var x1 = this.touch.x1,
        y1 = this.touch.y1,
        x2 = this.touch.x2,
        y2 = this.touch.y2;

    if ((this.touch.time && (Date.now() - this.touch.time >= this.HOLD_DELAY)) &&
        (Math.abs(x1 - x2) <= this.HOLD_TOLERANCE) &&
        (Math.abs(y1 - y2) <= this.HOLD_TOLERANCE)) {
      this.touch.type = 'taphold';
      this._triggerEvent(e);
    }
  },

  _direction: function(x1,x2, y1,y2) {
    var xDelta = Math.abs(x1 - x2),
        yDelta = Math.abs(y1 - y2);
    if (xDelta >= yDelta) {
      return (x1 - x2 > 0 ? 'left' : 'right');
    } else {
      return (y1 - y2 > 0 ? 'up' : 'down');
    }
  },

  // customizations
  preventScroll: function() { return false; },
  HOLD_DELAY: 750,
  DOUBLE_DELAY: 250,
  HOLD_TOLERANCE: 10,
  SWIPE_TOLERANCE: 60,
  TAP_TOLERANCE: 10,
  RESPONSIVE_TAP: true,
  GESTURE_TOLERANCE: 20
});
<commit_msg>bug: if no move happens with a taphold, the taphold test fails<commit_after>/* This was inspired by Zepto.js */
Wheel.View.subclass('Wheel.EventManager', {
  initializeDom: function(opts) {
    this.optionize(opts);
    this.$ = $(document);
  },

  listen: function() {
    var self = this;
    this.$.on('dragstart', function(e) {
      self.onDragStart(e);
    });
  },

  onDragStart: function(e) {
    e.preventDefault();
    this._setTarget(e);
    this.touch.type = 'drag';
  },

  onStart: function(e) {
    this.touch.ctrl = e.ctrlKey;
    e = this._normalizeEvent(e);
    this._setTarget(e);

    // clear existing timeout because there is a new touch start event
    // timeouts are set for 'taphold' and/or for 'tap'
    this._clearTimeout();

    // set up the touch object
    this.touch.x1 = e.pageX;
    this.touch.y1 = e.pageY;
    this.touch.time = Date.now();

    if (!this._testDoubleTap() && !this.touch.ctrl) {
      // wait to see if this is a taphold event
      var self = this;
      this.touchTimout = setTimeout(function() {
        self._testTouchhold(e);
      }, this.HOLD_DELAY);
    }
  },

  onMove: function(e) {
    this._handleDragMove(e);
    if (this.touch.type) { return; }
    e = this._unpackEvent(e);
    this._testSwipe(e);
  },

  onEnd: function(e) {
    this._handleDragEnd(e);
    if (this.touch.type || this.touch.ctrl) {
      this._resetTouch();
    } else {
      e = this._unpackEvent(e);
      this._handleTap(e);
    }
  },

  _triggerEvent: function(originalEvent, altType) {
    if (!this.target) { return; }
    var x = 'x2' in this.touch ? this.touch.x2 : this.touch.x1;
    var y = 'y2' in this.touch ? this.touch.y2 : this.touch.y1;

    this.target.trigger($.Event(altType || this.touch.type, {
      originalEvent: originalEvent,
      pageX: x,
      pageY: y
    }));
  },

  _setTarget: function(e) {
    var node = e.target;
    this.target = $('tagName' in node ? node : node.parentNode);
  },

  _clearTimeout: function() {
    this.touchTimeout && clearTimeout(this.touchTimeout);
  },

  _resetTouch: function() {
    this.lastTouch = this.touch;
    this.lastTouch.target = this.target;
    this.target = null;
    this.touch = {};
  },

  _normalizeEvent: function(e) {
    return e;
  },

  _unpackEvent: function(e) {
    e = this._normalizeEvent(e);

    if (e.type !== 'touchend') {
      this.touch.x2 = e.pageX >= 0 ? e.pageX : this.touch.x1;
      this.touch.y2 = e.pageY >= 0 ? e.pageY : this.touch.y1;
    }

    return e;
  },

  _handleTap: function(e) {
    if (this.RESPONSIVE_TAP) {
      this._onTap(e);
    } else {
      // wait to see if it is a tap, or if it is a double tap
      var self = this;
      this.touchTimeout = setTimeout(function(){
        self._onTap(e);
      }, this.DOUBLE_DELAY);
    }
  },

  _handleDragMove: function(e) {
    if (this.touch.type && this.touch.type === 'drag') {
      e.preventDefault();
      e = this._unpackEvent(e);
      this._triggerEvent(e, 'dragmove');
    }
  },

  _handleDragEnd: function(e) {
    if (this.touch.type && this.touch.type === 'drag') {
      e = this._unpackEvent(e);
      this._triggerEvent(e, 'dragend');
    }
  },

  _onTap: function(e) {
    this.touchTimeout = null;
    if ( this.target && e.target === this.target[0] &&
          this.TAP_TOLERANCE >= Math.abs(this.touch.x1 - this.touch.x2) &&
          this.TAP_TOLERANCE >= Math.abs(this.touch.y1 - this.touch.y2) ) {
      this.touch.type = 'tap';
      this._triggerEvent(e);
    }
    this._resetTouch();
  },

  _testDoubleTap: function(e) {
    // what about checking for distance from original tap
    var delta = this.lastTouch && this.lastTouch.time ? this.touch.time - this.lastTouch.time : 0;
    if (delta > 0 && delta <= this.DOUBLE_DELAY) {
      this.touch.type = 'doubletap';
      this._triggerEvent(e);
      return true;
    }
  },

  _testSwipe: function(e) {
    // if we have moved a certain distance, call it a swipe
    if (Math.abs(this.touch.x1 - this.touch.x2) >= this.SWIPE_TOLERANCE ||
        Math.abs(this.touch.y1 - this.touch.y2) >= this.SWIPE_TOLERANCE) {
      this.touch.type = 'swipe';
      var _direction = this._direction(this.touch.x1, this.touch.x2, this.touch.y1, this.touch.y2);
      if ( this.preventScroll(_direction) ) {
        e.preventDefault();
      }
      this._triggerEvent(e);
      this._triggerEvent(e, "swipe" + _direction);
    }
  },

  _testTouchhold: function(e) {
    var x1 = this.touch.x1,
        y1 = this.touch.y1;
        x2 = ('x2' in this.touch) ? this.touch.x2 : x1;
        y2 = ('y2' in this.touch) ? this.touch.y2 : y1;

    if ((this.touch.time && (Date.now() - this.touch.time >= this.HOLD_DELAY)) &&
        (Math.abs(x1 - x2) <= this.HOLD_TOLERANCE) &&
        (Math.abs(y1 - y2) <= this.HOLD_TOLERANCE)) {
      this.touch.type = 'taphold';
      this._triggerEvent(e);
    }
  },

  _direction: function(x1,x2, y1,y2) {
    var xDelta = Math.abs(x1 - x2),
        yDelta = Math.abs(y1 - y2);
    if (xDelta >= yDelta) {
      return (x1 - x2 > 0 ? 'left' : 'right');
    } else {
      return (y1 - y2 > 0 ? 'up' : 'down');
    }
  },

  // customizations
  preventScroll: function() { return false; },
  HOLD_DELAY: 750,
  DOUBLE_DELAY: 250,
  HOLD_TOLERANCE: 10,
  SWIPE_TOLERANCE: 60,
  TAP_TOLERANCE: 10,
  RESPONSIVE_TAP: true,
  GESTURE_TOLERANCE: 20
});
<|endoftext|>


<commit_before>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
import React from 'react';
import {
  values,
  throttle
} from 'lodash';
import { connect } from 'react-redux';

import { toggleUISidebar } from '../actions';

import Navigation from './navigation';
import NavigationItem from './navigation-item';
import CurrentUser from './current-user';
import TagCloud from './tag-cloud';
import SidebarFollowedTags from './sidebar-followed-tags';
import currentUserSelector from '../selectors/currentUser';
import createSelector from '../selectors/createSelector';

class Sidebar extends React.Component {
  static displayName = 'Sidebar';

  state = {
    clientWidth: 0
  };

  componentDidMount () {
    window.addEventListener('resize', this.toggleVisible);
    document.addEventListener('DOMContentLoaded', this.toggleVisible);
  }

  componentWillUnmount () {
    window.removeEventListener('resize', this.toggleVisible);
    document.removeEventListener('DOMContentLoaded', this.toggleVisible);
  }

  toggleVisible = throttle(() => {
    const {
      dispatch,
      ui
    } = this.props;
    const breakpointWidth = 960;

    if (typeof document == 'undefined') {
      return;
    }

    const clientWidth = document.body.clientWidth;

    if (clientWidth == this.state.clientWidth) {
      return;
    }

    if (ui.get('sidebarIsVisible') && (clientWidth <= breakpointWidth) && (clientWidth < this.state.clientWidth)) {
      dispatch(toggleUISidebar(false));
    }

    if (!ui.get('sidebarIsVisible') && (clientWidth >= breakpointWidth) && (clientWidth > this.state.clientWidth)) {
      dispatch(toggleUISidebar(true));
    }

    this.setState({
      clientWidth: clientWidth
    });
  }, 100);

  render() {
    const {
      ui,
      is_logged_in
    } = this.props;
    let sidebarClassName = ['sidebar'];

    if (!is_logged_in) {
      return null;
    }

    if (ui.get('sidebarIsVisible')) {
      sidebarClassName.push('sidebar-visible');
    }

    let current_user = this.props.current_user.toJS();

    let followedTags = values(current_user.followed_hashtags);
    let followedSchools = values(current_user.followed_schools);
    let followedGeotags = values(current_user.followed_geotags);
    let showLikes = (current_user.likes && current_user.likes.length > 0);
    let showFavorites = (current_user.favourites && current_user.favourites.length > 0);
    let showFollowedTags = !!followedTags.length || !!followedSchools.length || !!followedGeotags.length;
    let showUsedTags = current_user.hashtags && !!current_user.hashtags.length;

    return (

        <Navigation className="navigation-first">
          <NavigationItem enabled to="/" icon="public">News Feed</NavigationItem>

            <CurrentUser user={current_user.user} />

          {showLikes &&
            <NavigationItem
              enabled
              icon="favorite"
              to={`/user/${current_user.user.username}/likes`}
            >
              My Likes
            </NavigationItem>
          }
          {showFavorites &&
            <NavigationItem
              enabled
              icon="star"
              to={`/user/${current_user.user.username}/favorites`}
            >
              My Favorites
            </NavigationItem>
          }
        </Navigation>

        {showFollowedTags &&

I follow

            <Navigation>
              <SidebarFollowedTags geotags={followedGeotags} hashtags={followedTags} schools={followedSchools} />
            </Navigation>

        }

        {showUsedTags &&

I post to

              <TagCloud
                hashtags={current_user.hashtags}
                schools={[]}
                geotags={[]}
                truncated
              />

        }

    );
  }
}

const selector = createSelector(
  state => state.get('ui'),
  currentUserSelector,
  (ui, current_user) => ({
    ui,
    ...current_user
  })
);

export default connect(selector)(Sidebar);
<commit_msg>Added missing schools and geotags to sidebar<commit_after>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
import React from 'react';
import {
  values,
  throttle
} from 'lodash';
import { connect } from 'react-redux';

import { toggleUISidebar } from '../actions';

import Navigation from './navigation';
import NavigationItem from './navigation-item';
import CurrentUser from './current-user';
import TagCloud from './tag-cloud';
import SidebarFollowedTags from './sidebar-followed-tags';
import currentUserSelector from '../selectors/currentUser';
import createSelector from '../selectors/createSelector';

class Sidebar extends React.Component {
  static displayName = 'Sidebar';

  state = {
    clientWidth: 0
  };

  componentDidMount () {
    window.addEventListener('resize', this.toggleVisible);
    document.addEventListener('DOMContentLoaded', this.toggleVisible);
  }

  componentWillUnmount () {
    window.removeEventListener('resize', this.toggleVisible);
    document.removeEventListener('DOMContentLoaded', this.toggleVisible);
  }

  toggleVisible = throttle(() => {
    const {
      dispatch,
      ui
    } = this.props;
    const breakpointWidth = 960;

    if (typeof document == 'undefined') {
      return;
    }

    const clientWidth = document.body.clientWidth;

    if (clientWidth == this.state.clientWidth) {
      return;
    }

    if (ui.get('sidebarIsVisible') && (clientWidth <= breakpointWidth) && (clientWidth < this.state.clientWidth)) {
      dispatch(toggleUISidebar(false));
    }

    if (!ui.get('sidebarIsVisible') && (clientWidth >= breakpointWidth) && (clientWidth > this.state.clientWidth)) {
      dispatch(toggleUISidebar(true));
    }

    this.setState({
      clientWidth: clientWidth
    });
  }, 100);

  render() {
    const {
      ui,
      is_logged_in
    } = this.props;
    let sidebarClassName = ['sidebar'];

    if (!is_logged_in) {
      return null;
    }

    if (ui.get('sidebarIsVisible')) {
      sidebarClassName.push('sidebar-visible');
    }

    let current_user = this.props.current_user.toJS();

    let followedTags = values(current_user.followed_hashtags);
    let followedSchools = values(current_user.followed_schools);
    let followedGeotags = values(current_user.followed_geotags);
    let showLikes = (current_user.likes && current_user.likes.length > 0);
    let showFavorites = (current_user.favourites && current_user.favourites.length > 0);
    let showFollowedTags = !!followedTags.length || !!followedSchools.length || !!followedGeotags.length;
    let showUsedTags = current_user.hashtags && !!current_user.hashtags.length;

    return (

        <Navigation className="navigation-first">
          <NavigationItem enabled to="/" icon="public">News Feed</NavigationItem>

            <CurrentUser user={current_user.user} />

          {showLikes &&
            <NavigationItem
              enabled
              icon="favorite"
              to={`/user/${current_user.user.username}/likes`}
            >
              My Likes
            </NavigationItem>
          }
          {showFavorites &&
            <NavigationItem
              enabled
              icon="star"
              to={`/user/${current_user.user.username}/favorites`}
            >
              My Favorites
            </NavigationItem>
          }
        </Navigation>

        {showFollowedTags &&

I follow

            <Navigation>
              <SidebarFollowedTags geotags={followedGeotags} hashtags={followedTags} schools={followedSchools} />
            </Navigation>

        }

        {showUsedTags &&

I post to

              <TagCloud
                hashtags={current_user.hashtags}
                schools={current_user.schools}
                geotags={current_user.geotags}
                truncated
              />

        }

    );
  }
}

const selector = createSelector(
  state => state.get('ui'),
  currentUserSelector,
  (ui, current_user) => ({
    ui,
    ...current_user
  })
);

export default connect(selector)(Sidebar);
<|endoftext|>


<commit_before>odoo.define('website_event.geolocation', function (require) {
'use strict';

var publicWidget = require('web.public.widget');

publicWidget.registry.visitor = publicWidget.Widget.extend({
    selector: ".oe_country_events, .country_events",

    /**
     * @override
     */
    start: function () {
        var defs = [this._super.apply(this, arguments)];
        var self = this;
        var $eventList = this.$('.country_events_list');
        this._originalContent = $eventList[0].outerHTML;
        defs.push(this._rpc({route: '/event/get_country_event_list'}).then(function (data) {
            if (data) {
                self._$loadedContent = $(data);
                $eventList.replaceWith(self._$loadedContent);
            }
        }));
        return Promise.all(defs);
    },
    /**
     * @override
     */
    destroy: function () {
        this._super.apply(this, arguments);
        this._$loadedContent.replaceWith(this._originalContent);
    },
});
});
<commit_msg>[FIX] website_event: restore 'local events' snippet in edit mode<commit_after>odoo.define('website_event.geolocation', function (require) {
'use strict';

var publicWidget = require('web.public.widget');

publicWidget.registry.visitor = publicWidget.Widget.extend({
    selector: ".oe_country_events, .country_events",
    disabledInEditableMode: false,

    /**
     * @override
     */
    start: function () {
        var defs = [this._super.apply(this, arguments)];
        var self = this;
        var $eventList = this.$('.country_events_list');
        this._originalContent = $eventList[0].outerHTML;
        defs.push(this._rpc({route: '/event/get_country_event_list'}).then(function (data) {
            if (data) {
                self._$loadedContent = $(data);
                $eventList.replaceWith(self._$loadedContent);
            }
        }));
        return Promise.all(defs);
    },
    /**
     * @override
     */
    destroy: function () {
        this._super.apply(this, arguments);
        this._$loadedContent.replaceWith(this._originalContent);
    },
});
});
<|endoftext|>


<commit_before>"use strict"

import test from 'tape'
import protochain from './'

test('protochain', t => {
  t.test('finds correct prototype chain', t => {
    let obj = {}
    t.deepEqual(protochain(obj), [Object.prototype])
    t.deepEqual(protochain(Object.create(obj)), [obj, Object.prototype])
    t.deepEqual(protochain(new Error('message')), [Error.prototype, Object.prototype])
    t.deepEqual(protochain(new TypeError('message')), [TypeError.prototype, Error.prototype, Object.prototype])
    t.deepEqual(protochain(new String()), [String.prototype, Object.prototype])
    t.deepEqual(protochain(new Number()), [Number.prototype, Object.prototype])
    t.deepEqual(protochain(new RegExp('abc')), [RegExp.prototype, Object.prototype])
    t.deepEqual(protochain(new Date()), [Date.prototype, Object.prototype])
    t.end()
  })

  t.test('null prototype is handled correctly', t => {
    let noProtoObject = Object.create(null)
    t.deepEqual(protochain(noProtoObject), [])
    t.deepEqual(protochain(Object.create(noProtoObject)), [noProtoObject])
    t.end()
  })

  t.test('non-object values cooerce to object counterparts correctly', t => {
    t.deepEqual(protochain('abc'), [String.prototype, Object.prototype])
    t.deepEqual(protochain(123), [Number.prototype, Object.prototype])
    t.deepEqual(protochain(/abc/), [RegExp.prototype, Object.prototype])
    t.deepEqual(protochain(true), [Boolean.prototype, Object.prototype])
    t.deepEqual(protochain(false), [Boolean.prototype, Object.prototype])
    // falsey values
    t.deepEqual(protochain(''), [String.prototype, Object.prototype])
    t.deepEqual(protochain(0), [Number.prototype, Object.prototype])
    t.end()
  })

  t.test('null values produce empty list', t => {
    t.deepEqual(protochain(), [])
    t.deepEqual(protochain(undefined), [])
    t.deepEqual(protochain(null), [])
    t.end()
  })

  t.test('examples', t => {
    t.test('ES5', t => {
      function Person() {}
      function FancyPerson() {
        Person.call(this)
      }
      FancyPerson.prototype = Object.create(Person.prototype)

      t.deepEquals(protochain(new Person()), [Person.prototype, Object.prototype])
      t.deepEquals(protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
      t.end()
    })
    t.test('ES6', t => {
      class Person {}
      t.deepEquals(protochain(new Person()), [Person.prototype, Object.prototype])

      class FancyPerson extends Person {}
      t.deepEquals(protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
      let foo = Symbol('foo')
      t.deepEquals(protochain(foo), [ {}, {} ])
      t.end()
    })
  })

  t.end()
})
<commit_msg>Strictly match prototype chain Array content.<commit_after>"use strict"

import test from 'tape'
import protochain from './'

test('protochain', t => {
  t.test('finds correct prototype chain', t => {
    let obj = {}
    strictEqualArray(t, protochain(obj), [Object.prototype])
    strictEqualArray(t, protochain(Object.create(obj)), [obj, Object.prototype])
    strictEqualArray(t, protochain(new Error('message')), [Error.prototype, Object.prototype])
    strictEqualArray(t, protochain(new TypeError('message')), [TypeError.prototype, Error.prototype, Object.prototype])
    strictEqualArray(t, protochain(new String()), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(new Number()), [Number.prototype, Object.prototype])
    strictEqualArray(t, protochain(new RegExp('abc')), [RegExp.prototype, Object.prototype])
    strictEqualArray(t, protochain(new Date()), [Date.prototype, Object.prototype])
    t.end()
  })

  t.test('null prototype is handled correctly', t => {
    let noProtoObject = Object.create(null)
    strictEqualArray(t, protochain(noProtoObject), [])
    strictEqualArray(t, protochain(Object.create(noProtoObject)), [noProtoObject])
    t.end()
  })

  t.test('non-object values cooerce to object counterparts correctly', t => {
    strictEqualArray(t, protochain('abc'), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(123), [Number.prototype, Object.prototype])
    strictEqualArray(t, protochain(/abc/), [RegExp.prototype, Object.prototype])
    strictEqualArray(t, protochain(true), [Boolean.prototype, Object.prototype])
    strictEqualArray(t, protochain(false), [Boolean.prototype, Object.prototype])
    strictEqualArray(t, protochain(''), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(0), [Number.prototype, Object.prototype])
    t.end()
  })

  t.test('null values produce empty list', t => {
    strictEqualArray(t, protochain(), [])
    strictEqualArray(t, protochain(undefined), [])
    strictEqualArray(t, protochain(null), [])
    t.end()
  })

  t.test('examples', t => {
    t.test('ES5', t => {
      function Person() {}
      function FancyPerson() {
        Person.call(this)
      }
      FancyPerson.prototype = Object.create(Person.prototype)

      strictEqualArray(t, protochain(new Person()), [Person.prototype, Object.prototype])
      strictEqualArray(t, protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
      t.end()
    })
    t.test('ES6', t => {
      // note this will in-fact be compiled to ES5
      class Person {}
      strictEqualArray(t, protochain(new Person()), [Person.prototype, Object.prototype])

      class FancyPerson extends Person {}
      strictEqualArray(t, protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
      let foo = Symbol('foo')
      strictEqualArray(t, protochain(foo), [ Symbol.prototype, Object.prototype ])
      t.end()
    })
  })

  t.end()
})

function strictEqualArray(t, a, b) {
  a.forEach((item, index) => t.strictEqual(a[index], b[index], `strictEqual at index ${index}`))
  t.equal(a.length, b.length, 'same length')
}
<|endoftext|>


<commit_before>import {Template} from 'meteor/templating';
import {ReactiveDict} from 'meteor/reactive-dict';
import {Tracker} from 'meteor/tracker';

import {Games} from '../api/games.js';
import {Actions} from '../api/actions.js';
import {Pokemon} from '../api/pokemon.js';
import {Pokedex} from '../api/pokedex.js';

import './player.js';
import './chatbox.js';
import './game_page.html';

Template.game_page.onCreated(function() {
    const self = this;

    Meteor.subscribe('game', FlowRouter.getParam('_id'), function() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        let messages = [];
        for (let i=0;i<game.states.length;i++)
            messages = messages.concat(game.states[i].messages);
        console.log(messages);
        self.state.set('queue', messages);
    });
    Meteor.subscribe('chat', FlowRouter.getParam('_id'));
    Meteor.subscribe('actions', FlowRouter.getParam('_id'));
    Meteor.subscribe('results', FlowRouter.getParam('_id'));
    Meteor.subscribe('pokemon_instances');
    Meteor.subscribe('moves');
    Meteor.subscribe('ce');
    Meteor.subscribe('cep');

    this.state = new ReactiveDict();
    if (!this.state.get('message')) this.state.set('message', 0);
    if (!this.state.get('queue')) this.state.set('queue', []);

    const handle = Games.find(FlowRouter.getParam('_id')).observeChanges({
        changed(id, game) {
            let messages = [];
            for (let i=0;i<game.states.length;i++)
                messages = messages.concat(game.states[i].messages);
            self.state.set('queue', messages);
        },
    });
});

Template.game_page.helpers({
    name(s) {
        return s.split('-').map(function(str) {
            return str.charAt(0).toUpperCase()+str.slice(1);
        }).join(' ');
    },
    game() {
        return Games.findOne(FlowRouter.getParam('_id'));
    },
    pokemon() {
        return Pokemon.find({owner: Meteor.userId()});
    },
    isHost() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && game.host == Meteor.userId();
    },
    isInGame() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && game.players.indexOf(Meteor.userId()) > -1;
    },
    canJoinGame() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && Meteor.userId() && game.players.length < 4 && game.players.indexOf(Meteor.userId()) == -1;
    },
    stateIsPreparation() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (game) return game.state == 0;
        return null;
    },
    messages() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && game.states[game.states.length-1].messages;
    },
    canMove() {
        const game = Games.findOne({_id: FlowRouter.getParam('_id'), players: Meteor.userId()});
        const instance = Template.instance();
        if (game) return game.state > 0 && Actions.find({user: Meteor.userId(), turn: game.turn, game: game._id}, {limit: 1}).count(true) == 0 && instance.state.get('message') >= instance.state.get('queue').length;
        return null;
    },
    moves() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (!game) return;
        const p = Pokemon.findOne({_id: game.pokemon[Meteor.userId()]});
        return p && p.moves;
    },
    actions() {
        return Actions.find({game:FlowRouter.getParam('_id')});
    },
    emptySlots() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (game) {
            var o = Array();
            for (var i = 0; i < 4-game.players.length; i++) {
                o[i] = '';
            }
            return o
        }
        return null
    },
    turnOrder() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (game && game.state == 0) return game.players;
        return game && game.states[game.states.length-1].order;
    },
    text() {
        const instance = Template.instance();
        const q = instance.state.get('queue');
        const m = instance.state.get('message');
        if (m>=q.length) return 'Waiting on players...';
        return q[m];
    },
});

Template.game_page.events({
    'submit .join-game'(event) {
        event.preventDefault();
        const pokemon = event.target.pokemon.value;

        Meteor.call('game.join', FlowRouter.getParam('_id'), pokemon);
    },
    'submit .start-game'(event) {
        event.preventDefault();

        Meteor.call('game.start', FlowRouter.getParam('_id'));
    },
    'submit .delete-game'(event) {
        event.preventDefault();

        Meteor.call('game.delete', FlowRouter.getParam('_id'));
    },
    'click .action button'(event) {
        event.preventDefault();
        const action = event.target.value;
        Meteor.call('actions.insert', FlowRouter.getParam('_id'), action);
    },
    'mouseenter .action button'(event) {
        const action = event.target.value;
        const move = Pokedex.moveData(action);
        $('.category').text(move.category);
        $('.category').removeClass('cool beauty cute clever tough');
        $('.category').addClass(move.category);
        let appeal = "Appeal ";
        for (let i=0;i<move.appeal;i++) appeal += '♥';
        for (let i=move.appeal;i<8;i++) appeal += '♡';
        let jam = "Jam ";
        for (let i=0;i<move.jam;i++) jam += '♥';
        for (let i=move.jam;i<8;i++) jam += '♡';
        $('.appeal').text(appeal);
        $('.jam').text(jam);
        $('.flavor').text(move.flavor);
    },
    'click #dialog'(event) {
        const instance = Template.instance();
        const m = instance.state.get('message');
        const messages = instance.state.get('queue');
        while (m<messages.length) {
            instance.state.set('message', m+1);
            if (m+1>=messages.length) break;
            if (typeof messages[m+1] === 'string') break;
            const message = messages[m+1];
            switch (message.type) {
                case 'applause':
                    console.log(message.value);
            }
        }
    },
    'click #skip'(event) {
        const instance = Template.instance();
        const messages = instance.state.get('queue');
        instance.state.set('message', messages.length);
    },
    'click #back'(event) {
        const instance = Template.instance();
        const m = instance.state.get('message');
        if (m>0) {
            // for now its -2 to offset the +1 from normal click
            instance.state.set('message', m-2);
        }
    },
});

<commit_msg>lets stop spamming the log<commit_after>import {Template} from 'meteor/templating';
import {ReactiveDict} from 'meteor/reactive-dict';
import {Tracker} from 'meteor/tracker';

import {Games} from '../api/games.js';
import {Actions} from '../api/actions.js';
import {Pokemon} from '../api/pokemon.js';
import {Pokedex} from '../api/pokedex.js';

import './player.js';
import './chatbox.js';
import './game_page.html';

Template.game_page.onCreated(function() {
    const self = this;

    Meteor.subscribe('game', FlowRouter.getParam('_id'), function() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        let messages = [];
        for (let i=0;i<game.states.length;i++)
            messages = messages.concat(game.states[i].messages);
        self.state.set('queue', messages);
    });
    Meteor.subscribe('chat', FlowRouter.getParam('_id'));
    Meteor.subscribe('actions', FlowRouter.getParam('_id'));
    Meteor.subscribe('results', FlowRouter.getParam('_id'));
    Meteor.subscribe('pokemon_instances');
    Meteor.subscribe('moves');
    Meteor.subscribe('ce');
    Meteor.subscribe('cep');

    this.state = new ReactiveDict();
    if (!this.state.get('message')) this.state.set('message', 0);
    if (!this.state.get('queue')) this.state.set('queue', []);

    const handle = Games.find(FlowRouter.getParam('_id')).observeChanges({
        changed(id, game) {
            let messages = [];
            for (let i=0;i<game.states.length;i++)
                messages = messages.concat(game.states[i].messages);
            self.state.set('queue', messages);
        },
    });
});

Template.game_page.helpers({
    name(s) {
        return s.split('-').map(function(str) {
            return str.charAt(0).toUpperCase()+str.slice(1);
        }).join(' ');
    },
    game() {
        return Games.findOne(FlowRouter.getParam('_id'));
    },
    pokemon() {
        return Pokemon.find({owner: Meteor.userId()});
    },
    isHost() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && game.host == Meteor.userId();
    },
    isInGame() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && game.players.indexOf(Meteor.userId()) > -1;
    },
    canJoinGame() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && Meteor.userId() && game.players.length < 4 && game.players.indexOf(Meteor.userId()) == -1;
    },
    stateIsPreparation() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (game) return game.state == 0;
        return null;
    },
    messages() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        return game && game.states[game.states.length-1].messages;
    },
    canMove() {
        const game = Games.findOne({_id: FlowRouter.getParam('_id'), players: Meteor.userId()});
        const instance = Template.instance();
        if (game) return game.state > 0 && Actions.find({user: Meteor.userId(), turn: game.turn, game: game._id}, {limit: 1}).count(true) == 0 && instance.state.get('message') >= instance.state.get('queue').length;
        return null;
    },
    moves() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (!game) return;
        const p = Pokemon.findOne({_id: game.pokemon[Meteor.userId()]});
        return p && p.moves;
    },
    actions() {
        return Actions.find({game:FlowRouter.getParam('_id')});
    },
    emptySlots() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (game) {
            var o = Array();
            for (var i = 0; i < 4-game.players.length; i++) {
                o[i] = '';
            }
            return o
        }
        return null
    },
    turnOrder() {
        const game = Games.findOne(FlowRouter.getParam('_id'));
        if (game && game.state == 0) return game.players;
        return game && game.states[game.states.length-1].order;
    },
    text() {
        const instance = Template.instance();
        const q = instance.state.get('queue');
        const m = instance.state.get('message');
        if (m>=q.length) return 'Waiting on players...';
        return q[m];
    },
});

Template.game_page.events({
    'submit .join-game'(event) {
        event.preventDefault();
        const pokemon = event.target.pokemon.value;

        Meteor.call('game.join', FlowRouter.getParam('_id'), pokemon);
    },
    'submit .start-game'(event) {
        event.preventDefault();

        Meteor.call('game.start', FlowRouter.getParam('_id'));
    },
    'submit .delete-game'(event) {
        event.preventDefault();

        Meteor.call('game.delete', FlowRouter.getParam('_id'));
    },
    'click .action button'(event) {
        event.preventDefault();
        const action = event.target.value;
        Meteor.call('actions.insert', FlowRouter.getParam('_id'), action);
    },
    'mouseenter .action button'(event) {
        const action = event.target.value;
        const move = Pokedex.moveData(action);
        $('.category').text(move.category);
        $('.category').removeClass('cool beauty cute clever tough');
        $('.category').addClass(move.category);
        let appeal = "Appeal ";
        for (let i=0;i<move.appeal;i++) appeal += '♥';
        for (let i=move.appeal;i<8;i++) appeal += '♡';
        let jam = "Jam ";
        for (let i=0;i<move.jam;i++) jam += '♥';
        for (let i=move.jam;i<8;i++) jam += '♡';
        $('.appeal').text(appeal);
        $('.jam').text(jam);
        $('.flavor').text(move.flavor);
    },
    'click #dialog'(event) {
        const instance = Template.instance();
        const m = instance.state.get('message');
        const messages = instance.state.get('queue');
        while (m<messages.length) {
            instance.state.set('message', m+1);
            if (m+1>=messages.length) break;
            if (typeof messages[m+1] === 'string') break;
            const message = messages[m+1];
            switch (message.type) {
                case 'applause':
                    console.log(message.value);
            }
        }
    },
    'click #skip'(event) {
        const instance = Template.instance();
        const messages = instance.state.get('queue');
        instance.state.set('message', messages.length);
    },
    'click #back'(event) {
        const instance = Template.instance();
        const m = instance.state.get('message');
        if (m>0) {
            // for now its -2 to offset the +1 from normal click
            instance.state.set('message', m-2);
        }
    },
});

<|endoftext|>


<commit_before>import React from 'react';
import {observe} from '../store';
import withState from './state';
import styled from 'styled-components';
import OdreianDate from 'odreian-date';

const moonPhase = date => [
	'🌕', '🌖', '🌗', '🌘', '🌑', '🌒', '🌓', '🌔',
][Math.floor(date * 8/30)];

const compassDir = heading => [
	'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW',
][Math.round(heading * 16/360) % 16];

const WindArrow = styled.span`
display: inline-block;
transform: rotate(${({heading}) => heading}deg);
`;

const WindDirection = ({heading}) =>
	<WindArrow heading={heading - 90}>➳</WindArrow>
	{compassDir((heading + 180) % 360)}
;

const Weather = observe((props, {subscribe}) => {
	const {temperature, humidity, windHeading, windSpeed} = subscribe('weather');
	const date = new OdreianDate(subscribe('date')).dateIndex;
	return

{temperature}℃

{humidity}% humidity

<WindDirection heading={windHeading} /> {windSpeed}KN

{moonPhase(date)}

;
});

const FixedWidthLabel = styled.label`
display: inline-block;
width: ${({size = 3.5}) => size}em;
`;

const WeatherForm = withState(
	({weather}) => weather,
	({weather, onSubmit}, state, setState) =>

			<FixedWidthLabel>{state.temperature}℃</FixedWidthLabel>
			<input
				type='range' min={-20} max={60}
				placeholder='temperature'
				value={state.temperature}
				onChange={ev => setState({temperature: ev.target.valueAsNumber})} />

			<FixedWidthLabel>{state.humidity}%</FixedWidthLabel>
			<input
				type='range' min={0} max={100} step={5}
				placeholder='humidity'
				value={state.humidity}
				onChange={ev => setState({humidity: ev.target.valueAsNumber})} />

			<FixedWidthLabel><WindDirection heading={state.windHeading} /></FixedWidthLabel>
			<input
				type='range' min={0} max={359} step={5}
				placeholder='windHeading'
				value={state.windHeading}
				onChange={ev => setState({windHeading: ev.target.valueAsNumber})} />

			<FixedWidthLabel>{state.windSpeed}KN</FixedWidthLabel>
			<input
				type='range' min={0} max={120} step={5}
				placeholder='windSpeed'
				value={state.windSpeed}
				onChange={ev => setState({windSpeed: ev.target.valueAsNumber})} />

		<button onClick={() => onSubmit(state)}>Set</button>

);

const WeatherFormConnector = observe((props, {subscribe, dispatch}) => <WeatherForm
	weather={subscribe('weather')}
	onSubmit={weather => dispatch('weather', () => weather)}
/>);

const WeatherControl = () =>

	<Weather />
	<WeatherFormConnector />

export {
	WeatherControl as control,
	Weather as display
};
<commit_msg>weather emoji<commit_after>import React from 'react';
import {observe} from '../store';
import withState from './state';
import styled from 'styled-components';
import OdreianDate from 'odreian-date';

const moonPhase = date => [
	'🌕', '🌖', '🌗', '🌘', '🌑', '🌒', '🌓', '🌔',
][Math.floor(date * 8/30)];

const compassDir = heading => [
	'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW',
][Math.round(heading * 16/360) % 16];

const weatherCondition = ({temperature, humidity}) => [
	['❄','🌤','☀','☀','🏜'],
	['🏔','🌥','⛅','🌤','🌤'],
	['💨','☁','🌦','🌦','🌩'],
	['🌨','🌧','🌧','⛈','⛈'],
]
[Math.min(3, Math.floor(humidity * 4 / 100))]
[Math.floor((20 + temperature) * 4 / 80)];

const WindArrow = styled.span`
display: inline-block;
transform: rotate(${({heading}) => heading}deg);
`;

const WindDirection = ({heading}) =>
	<WindArrow heading={heading - 90}>➳</WindArrow>
	{compassDir((heading + 180) % 360)}
;

const Weather = observe((props, {subscribe}) => {
	const {temperature, humidity, windHeading, windSpeed} = subscribe('weather');
	const date = new OdreianDate(subscribe('date')).dateIndex;
	return

{weatherCondition({temperature, humidity})} {temperature}℃

<WindDirection heading={windHeading} /> {windSpeed}KN

{moonPhase(date)}

;
});

const FixedWidthLabel = styled.label`
display: inline-block;
width: ${({size = 3.5}) => size}em;
`;

const WeatherForm = withState(
	({weather}) => weather,
	({weather, onSubmit}, state, setState) =>

			<FixedWidthLabel>{state.temperature}℃</FixedWidthLabel>
			<input
				type='range' min={-20} max={60}
				placeholder='temperature'
				value={state.temperature}
				onChange={ev => setState({temperature: ev.target.valueAsNumber})} />

			<FixedWidthLabel>{state.humidity}%</FixedWidthLabel>
			<input
				type='range' min={0} max={100} step={5}
				placeholder='humidity'
				value={state.humidity}
				onChange={ev => setState({humidity: ev.target.valueAsNumber})} />

			<FixedWidthLabel><WindDirection heading={state.windHeading} /></FixedWidthLabel>
			<input
				type='range' min={0} max={359} step={5}
				placeholder='windHeading'
				value={state.windHeading}
				onChange={ev => setState({windHeading: ev.target.valueAsNumber})} />

			<FixedWidthLabel>{state.windSpeed}KN</FixedWidthLabel>
			<input
				type='range' min={0} max={120} step={5}
				placeholder='windSpeed'
				value={state.windSpeed}
				onChange={ev => setState({windSpeed: ev.target.valueAsNumber})} />

		<button onClick={() => onSubmit(state)}>Set</button>

);

const WeatherFormConnector = observe((props, {subscribe, dispatch}) => <WeatherForm
	weather={subscribe('weather')}
	onSubmit={weather => dispatch('weather', () => weather)}
/>);

const WeatherControl = () =>

	<Weather />
	<WeatherFormConnector />

export {
	WeatherControl as control,
	Weather as display
};
<|endoftext|>


<commit_before>/**
 * Loops an array
 * @param   { Array } els - collection of items
 * @param   {Function} fn - callback function
 * @returns { Array } the array looped
 */
function each(els, fn) {
  for (var i = 0, len = (els || []).length, el; i < len; i++) {
    el = els[i]
    // return false -> remove current item during loop
    if (el != null && fn(el, i) === false) i--
  }
  return els
}

/**
 * Detect if the argument passed is a function
 * @param   { * } v - whatever you want to pass to this function
 * @returns { Boolean } -
 */
function isFunction(v) {
  return typeof v === T_FUNCTION || false   // avoid IE problems
}

/**
 * Remove any DOM attribute from a node
 * @param   { Object } dom - DOM node we want to update
 * @param   { String } name - name of the property we want to remove
 */
function remAttr(dom, name) {
  dom.removeAttribute(name)
}

/**
 * Convert a string containing dashes to camle case
 * @param   { String } string - input string
 * @returns { String } my-string -> myString
 */
function toCamel(string) {
  return string.replace(/(\-\w)/g, function(match) {
    return match.toUpperCase().replace('-', '')
  })
}

/**
 * Get the value of any DOM attribute on a node
 * @param   { Object } dom - DOM node we want to parse
 * @param   { String } name - name of the attribute we want to get
 * @returns { String | undefined } name of the node attribute whether it exists
 */
function getAttr(dom, name) {
  return dom.getAttribute(name)
}

/**
 * Set any DOM attribute
 * @param { Object } dom - DOM node we want to update
 * @param { String } name - name of the property we want to set
 * @param { String } val - value of the property we want to set
 */
function setAttr(dom, name, val) {
  dom.setAttribute(name, val)
}

/**
 * Check whether an object property could be overridden
 * @param   { Object }  obj - object to test
 * @param   { String }  prop - property to test
 * @returns {Boolean} is obj[prop] writable?
 */
function isWritable(obj, prop) {
  return !obj[prop] || Object.getOwnPropertyDescriptor(obj, prop).writable
}

/**
 * Detect the tag implementation by a DOM node
 * @param   { Object } dom - DOM node we need to parse to get its tag implementation
 * @returns { Object } it returns an object containing the implementation of a custom tag (template and boot function)
 */
function getTag(dom) {
  return dom.tagName && __tagImpl[getAttr(dom, RIOT_TAG) || dom.tagName.toLowerCase()]
}
/**
 * Add a child tag to its parent into the `tags` object
 * @param   { Object } tag - child tag instance
 * @param   { String } tagName - key where the new tag will be stored
 * @param   { Object } parent - tag instance where the new child tag will be included
 */
function addChildTag(tag, tagName, parent) {
  var cachedTag = parent.tags[tagName]

  // if there are multiple children tags having the same name
  if (cachedTag) {
    // if the parent tags property is not yet an array
    // create it adding the first cached tag
    if (!isArray(cachedTag))
      // don't add the same tag twice
      if (cachedTag !== tag)
        parent.tags[tagName] = [cachedTag]
    // add the new nested tag to the array
    if (!contains(parent.tags[tagName], tag))
      parent.tags[tagName].push(tag)
  } else {
    parent.tags[tagName] = tag
  }
}

/**
 * Move the position of a custom tag in its parent tag
 * @param   { Object } tag - child tag instance
 * @param   { String } tagName - key where the tag was stored
 * @param   { Number } newPos - index where the new tag will be stored
 */
function moveChildTag(tag, tagName, newPos) {
  var parent = tag.parent,
    tags
  // no parent no move
  if (!parent) return

  tags = parent.tags[tagName]

  if (isArray(tags))
    tags.splice(newPos, 0, tags.splice(tags.indexOf(tag), 1)[0])
  else addChildTag(tag, tagName, parent)
}

/**
 * Create a new child tag including it correctly into its parent
 * @param   { Object } child - child tag implementation
 * @param   { Object } opts - tag options containing the DOM node where the tag will be mounted
 * @param   { String } innerHTML - inner html of the child node
 * @param   { Object } parent - instance of the parent tag including the child custom tag
 * @returns { Object } instance of the new child tag just created
 */
function initChildTag(child, opts, innerHTML, parent) {
  var tag = new Tag(child, opts, innerHTML),
    tagName = getTagName(opts.root),
    ptag = getImmediateCustomParentTag(parent)
  // fix for the parent attribute in the looped elements
  tag.parent = ptag
  // store the real parent tag
  // in some cases this could be different from the custom parent tag
  // for example in nested loops
  tag._parent = parent

  // add this tag to the custom parent tag
  addChildTag(tag, tagName, ptag)
  // and also to the real parent tag
  if (ptag !== parent)
    addChildTag(tag, tagName, parent)
  // empty the child node once we got its template
  // to avoid that its children get compiled multiple times
  opts.root.innerHTML = ''

  return tag
}

/**
 * Loop backward all the parents tree to detect the first custom parent tag
 * @param   { Object } tag - a Tag instance
 * @returns { Object } the instance of the first custom parent tag found
 */
function getImmediateCustomParentTag(tag) {
  var ptag = tag
  while (!getTag(ptag.root)) {
    if (!ptag.parent) break
    ptag = ptag.parent
  }
  return ptag
}

/**
 * Helper function to set an immutable property
 * @param   { Object } el - object where the new property will be set
 * @param   { String } key - object key where the new property will be stored
 * @param   { * } value - value of the new property
* @param   { Object } options - set the propery overriding the default options
 * @returns { Object } - the initial object
 */
function defineProperty(el, key, value, options) {
  Object.defineProperty(el, key, extend({
    value: value,
    enumerable: false,
    writable: false,
    configurable: false
  }, options))
  return el
}

/**
 * Get the tag name of any DOM node
 * @param   { Object } dom - DOM node we want to parse
 * @returns { String } name to identify this dom node in riot
 */
function getTagName(dom) {
  var child = getTag(dom),
    namedTag = getAttr(dom, 'name'),
    tagName = namedTag && !contains(namedTag, brackets(0)) ?
                namedTag :
              child ? child.name : dom.tagName.toLowerCase()

  return tagName
}

/**
 * Extend any object with other properties
 * @param   { Object } src - source object
 * @returns { Object } the resulting extended object
 *
 * var obj = { foo: 'baz' }
 * extend(obj, {bar: 'bar', foo: 'bar'})
 * console.log(obj) => {bar: 'bar', foo: 'bar'}
 *
 */
function extend(src) {
  var obj, args = arguments
  for (var i = 1; i < args.length; ++i) {
    if (obj = args[i]) {
      for (var key in obj) {
        // check if this property of the source object could be overridden
        if (isWritable(src, key))
          src[key] = obj[key]
      }
    }
  }
  return src
}

/**
 * Check whether an array contains an item
 * @param   { Array } arr - target array
 * @param   { * } item - item to test
 * @returns { Boolean } Does 'arr' contain 'item'?
 */
function contains(arr, item) {
  return ~arr.indexOf(item)
}

/**
 * Check whether an object is a kind of array
 * @param   { * } a - anything
 * @returns {Boolean} is 'a' an array?
 */
function isArray(a) { return Array.isArray(a) || a instanceof Array }

/**
 * With this function we avoid that the internal Tag methods get overridden
 * @param   { Object } data - options we want to use to extend the tag instance
 * @returns { Object } clean object without containing the riot internal reserved words
 */
function cleanUpData(data) {
  if (!(data instanceof Tag) && !(data && typeof data.trigger == T_FUNCTION)) return data

  var o = {}
  for (var key in data) {
    if (!contains(RESERVED_WORDS_BLACKLIST, key))
      o[key] = data[key]
  }
  return o
}

/**
 * Walk down recursively all the children tags starting dom node
 * @param   { Object }   dom - starting node where we will start the recursion
 * @param   { Function } fn - callback to transform the child node just found
 */
function walk(dom, fn) {
  if (dom) {
    // stop the recursion
    if (fn(dom) === false) return
    else {
      dom = dom.firstChild

      while (dom) {
        walk(dom, fn)
        dom = dom.nextSibling
      }
    }
  }
}

/**
 * Minimize risk: only zero or one _space_ between attr & value
 * @param   { String }   html - html string we want to parse
 * @param   { Function } fn - callback function to apply on any attribute found
 */
function walkAttributes(html, fn) {
  var m,
    re = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g

  while (m = re.exec(html)) {
    fn(m[1].toLowerCase(), m[2] || m[3] || m[4])
  }
}

/**
 * Check whether a DOM node is in stub mode, useful for the riot 'if' directive
 * @param   { Object }  dom - DOM node we want to parse
 * @returns { Boolean } -
 */
function isInStub(dom) {
  while (dom) {
    if (dom.inStub) return true
    dom = dom.parentNode
  }
  return false
}

/**
 * Create a generic DOM node
 * @param   { String } name - name of the DOM node we want to create
 * @returns { Object } DOM node just created
 */
function mkEl(name) {
  return document.createElement(name)
}

/**
 * Replace the yield tag from any tag template with the innerHTML of the
 * original tag in the page
 * @param   { String } tmpl - tag implementation template
 * @param   { String } innerHTML - original content of the tag in the DOM
 * @returns { String } tag template updated without the yield tag
 */
function replaceYield(tmpl, innerHTML) {
  return tmpl.replace(/<(yield)\/?>(<\/\1>)?/gi, innerHTML || '')
}

/**
 * Shorter and fast way to select multiple nodes in the DOM
 * @param   { String } selector - DOM selector
 * @param   { Object } ctx - DOM node where the targets of our search will is located
 * @returns { Object } dom nodes found
 */
function $$(selector, ctx) {
  return (ctx || document).querySelectorAll(selector)
}

/**
 * Shorter and fast way to select a single node in the DOM
 * @param   { String } selector - unique dom selector
 * @param   { Object } ctx - DOM node where the target of our search will is located
 * @returns { Object } dom node found
 */
function $(selector, ctx) {
  return (ctx || document).querySelector(selector)
}

/**
 * Simple object prototypal inheritance
 * @param   { Object } parent - parent object
 * @returns { Object } child instance
 */
function inherit(parent) {
  function Child() {}
  Child.prototype = parent
  return new Child()
}

/**
 * Get the name property needed to identify a DOM node in riot
 * @param   { Object } dom - DOM node we need to parse
 * @returns { String | undefined } give us back a string to identify this dom node
 */
function getNamedKey(dom) {
  return getAttr(dom, 'id') || getAttr(dom, 'name')
}

/**
 * Set the named properties of a tag element
 * @param { Object } dom - DOM node we need to parse
 * @param { Object } parent - tag instance where the named dom element will be eventually added
 * @param { Array } keys - list of all the tag instance properties
 */
function setNamed(dom, parent, keys) {
  // get the key value we want to add to the tag instance
  var key = getNamedKey(dom),
    // add the node detected to a tag instance using the named property
    add = function(value) {
      // avoid to override the tag properties already set
      if (contains(keys, key)) return
      // check whether this value is an array
      var isArr = isArray(value)
      // if the key was never set
      if (!value)
        // set it once on the tag instance
        parent[key] = dom
      // if it was an array and not yet set
      else if (!isArr || isArr && !contains(value, dom)) {
        // add the dom node into the array
        if (isArr)
          value.push(dom)
        else
          parent[key] = [value, dom]
      }
    }

  // skip the elements with no named properties
  if (!key) return

  // check whether this key has been already evaluated
  if (contains(key, brackets(0)))
    // wait the first updated event only once
    parent.one('updated', function() {
      key = getNamedKey(dom)
      add(parent[key])
    })
  else
    add(parent[key])

}

/**
 * Faster String startsWith alternative
 * @param   { String } src - source string
 * @param   { String } str - test string
 * @returns { Boolean } -
 */
function startsWith(src, str) {
  return src.slice(0, str.length) === str
}

/**
 * Function needed to inject in runtime the custom tags css
 */
var injectStyle = (function() {

  if (!window) return // skip injection on the server

  // create the style node
  var styleNode = mkEl('style'),
    placeholder = $('style[type=riot]'),
    head = document.head || document.getElementsByTagName('head')[0]

  setAttr(styleNode, 'type', 'text/css')

  // inject the new node into the DOM
  if (styleNode.styleSheet) {
    document.body.appendChild(styleNode)
  } else {
    if (placeholder) {
      placeholder.parentNode.insertBefore(styleNode, placeholder)
      placeholder.parentNode.removeChild(placeholder)
    } else head.appendChild(styleNode)
  }

  /**
   * This is the function exported that will be used to update the style tag just created
   * @param   { String } css [description]
   */
  return function(css) {
    if (styleNode.styleSheet)
      styleNode.styleSheet.cssText += css
    else
      styleNode.innerHTML += css
  }

})()

/**
 * Mount a tag creating new Tag instance
 * @param   { Object } root - dom node where the tag will be mounted
 * @param   { String } tagName - name of the riot tag we want to mount
 * @param   { Object } opts - options to pass to the Tag instance
 * @returns { Tag } a new Tag instance
 */
function mountTo(root, tagName, opts) {
  var tag = __tagImpl[tagName],
    // cache the inner HTML to fix #855
    innerHTML = root._innerHTML = root._innerHTML || root.innerHTML

  // clear the inner html
  root.innerHTML = ''

  if (tag && root) tag = new Tag(tag, { root: root, opts: opts }, innerHTML)

  if (tag && tag.mount) {
    tag.mount()
    // add this tag to the virtualDom variable
    if (!contains(__virtualDom, tag)) __virtualDom.push(tag)
  }

  return tag
}
<commit_msg>updated: restored isWritable helper<commit_after>/**
 * Loops an array
 * @param   { Array } els - collection of items
 * @param   {Function} fn - callback function
 * @returns { Array } the array looped
 */
function each(els, fn) {
  for (var i = 0, len = (els || []).length, el; i < len; i++) {
    el = els[i]
    // return false -> remove current item during loop
    if (el != null && fn(el, i) === false) i--
  }
  return els
}

/**
 * Detect if the argument passed is a function
 * @param   { * } v - whatever you want to pass to this function
 * @returns { Boolean } -
 */
function isFunction(v) {
  return typeof v === T_FUNCTION || false   // avoid IE problems
}

/**
 * Remove any DOM attribute from a node
 * @param   { Object } dom - DOM node we want to update
 * @param   { String } name - name of the property we want to remove
 */
function remAttr(dom, name) {
  dom.removeAttribute(name)
}

/**
 * Convert a string containing dashes to camle case
 * @param   { String } string - input string
 * @returns { String } my-string -> myString
 */
function toCamel(string) {
  return string.replace(/(\-\w)/g, function(match) {
    return match.toUpperCase().replace('-', '')
  })
}

/**
 * Get the value of any DOM attribute on a node
 * @param   { Object } dom - DOM node we want to parse
 * @param   { String } name - name of the attribute we want to get
 * @returns { String | undefined } name of the node attribute whether it exists
 */
function getAttr(dom, name) {
  return dom.getAttribute(name)
}

/**
 * Set any DOM attribute
 * @param { Object } dom - DOM node we want to update
 * @param { String } name - name of the property we want to set
 * @param { String } val - value of the property we want to set
 */
function setAttr(dom, name, val) {
  dom.setAttribute(name, val)
}

/**
 * Detect the tag implementation by a DOM node
 * @param   { Object } dom - DOM node we need to parse to get its tag implementation
 * @returns { Object } it returns an object containing the implementation of a custom tag (template and boot function)
 */
function getTag(dom) {
  return dom.tagName && __tagImpl[getAttr(dom, RIOT_TAG) || dom.tagName.toLowerCase()]
}
/**
 * Add a child tag to its parent into the `tags` object
 * @param   { Object } tag - child tag instance
 * @param   { String } tagName - key where the new tag will be stored
 * @param   { Object } parent - tag instance where the new child tag will be included
 */
function addChildTag(tag, tagName, parent) {
  var cachedTag = parent.tags[tagName]

  // if there are multiple children tags having the same name
  if (cachedTag) {
    // if the parent tags property is not yet an array
    // create it adding the first cached tag
    if (!isArray(cachedTag))
      // don't add the same tag twice
      if (cachedTag !== tag)
        parent.tags[tagName] = [cachedTag]
    // add the new nested tag to the array
    if (!contains(parent.tags[tagName], tag))
      parent.tags[tagName].push(tag)
  } else {
    parent.tags[tagName] = tag
  }
}

/**
 * Move the position of a custom tag in its parent tag
 * @param   { Object } tag - child tag instance
 * @param   { String } tagName - key where the tag was stored
 * @param   { Number } newPos - index where the new tag will be stored
 */
function moveChildTag(tag, tagName, newPos) {
  var parent = tag.parent,
    tags
  // no parent no move
  if (!parent) return

  tags = parent.tags[tagName]

  if (isArray(tags))
    tags.splice(newPos, 0, tags.splice(tags.indexOf(tag), 1)[0])
  else addChildTag(tag, tagName, parent)
}

/**
 * Create a new child tag including it correctly into its parent
 * @param   { Object } child - child tag implementation
 * @param   { Object } opts - tag options containing the DOM node where the tag will be mounted
 * @param   { String } innerHTML - inner html of the child node
 * @param   { Object } parent - instance of the parent tag including the child custom tag
 * @returns { Object } instance of the new child tag just created
 */
function initChildTag(child, opts, innerHTML, parent) {
  var tag = new Tag(child, opts, innerHTML),
    tagName = getTagName(opts.root),
    ptag = getImmediateCustomParentTag(parent)
  // fix for the parent attribute in the looped elements
  tag.parent = ptag
  // store the real parent tag
  // in some cases this could be different from the custom parent tag
  // for example in nested loops
  tag._parent = parent

  // add this tag to the custom parent tag
  addChildTag(tag, tagName, ptag)
  // and also to the real parent tag
  if (ptag !== parent)
    addChildTag(tag, tagName, parent)
  // empty the child node once we got its template
  // to avoid that its children get compiled multiple times
  opts.root.innerHTML = ''

  return tag
}

/**
 * Loop backward all the parents tree to detect the first custom parent tag
 * @param   { Object } tag - a Tag instance
 * @returns { Object } the instance of the first custom parent tag found
 */
function getImmediateCustomParentTag(tag) {
  var ptag = tag
  while (!getTag(ptag.root)) {
    if (!ptag.parent) break
    ptag = ptag.parent
  }
  return ptag
}

/**
 * Helper function to set an immutable property
 * @param   { Object } el - object where the new property will be set
 * @param   { String } key - object key where the new property will be stored
 * @param   { * } value - value of the new property
* @param   { Object } options - set the propery overriding the default options
 * @returns { Object } - the initial object
 */
function defineProperty(el, key, value, options) {
  Object.defineProperty(el, key, extend({
    value: value,
    enumerable: false,
    writable: false,
    configurable: false
  }, options))
  return el
}

/**
 * Get the tag name of any DOM node
 * @param   { Object } dom - DOM node we want to parse
 * @returns { String } name to identify this dom node in riot
 */
function getTagName(dom) {
  var child = getTag(dom),
    namedTag = getAttr(dom, 'name'),
    tagName = namedTag && !contains(namedTag, brackets(0)) ?
                namedTag :
              child ? child.name : dom.tagName.toLowerCase()

  return tagName
}

/**
 * Extend any object with other properties
 * @param   { Object } src - source object
 * @returns { Object } the resulting extended object
 *
 * var obj = { foo: 'baz' }
 * extend(obj, {bar: 'bar', foo: 'bar'})
 * console.log(obj) => {bar: 'bar', foo: 'bar'}
 *
 */
function extend(src) {
  var obj, args = arguments
  for (var i = 1; i < args.length; ++i) {
    if (obj = args[i]) {
      for (var key in obj) {
        // check if this property of the source object could be overridden
        if (isWritable(src, key))
          src[key] = obj[key]
      }
    }
  }
  return src
}

/**
 * Check whether an array contains an item
 * @param   { Array } arr - target array
 * @param   { * } item - item to test
 * @returns { Boolean } Does 'arr' contain 'item'?
 */
function contains(arr, item) {
  return ~arr.indexOf(item)
}

/**
 * Check whether an object is a kind of array
 * @param   { * } a - anything
 * @returns {Boolean} is 'a' an array?
 */
function isArray(a) { return Array.isArray(a) || a instanceof Array }

/**
 * Detect whether a property of an object could be overridden
 * @param   { Object }  obj - source object
 * @param   { String }  key - object property
 * @returns { Boolean } is this property writable?
 */
function isWritable(obj, key) {
  var props = Object.getOwnPropertyDescriptor(obj, key)
  return typeof obj[key] === T_UNDEF || props && props.writable
}

/**
 * With this function we avoid that the internal Tag methods get overridden
 * @param   { Object } data - options we want to use to extend the tag instance
 * @returns { Object } clean object without containing the riot internal reserved words
 */
function cleanUpData(data) {
  if (!(data instanceof Tag) && !(data && typeof data.trigger == T_FUNCTION)) return data

  var o = {}
  for (var key in data) {
    if (!contains(RESERVED_WORDS_BLACKLIST, key))
      o[key] = data[key]
  }
  return o
}

/**
 * Walk down recursively all the children tags starting dom node
 * @param   { Object }   dom - starting node where we will start the recursion
 * @param   { Function } fn - callback to transform the child node just found
 */
function walk(dom, fn) {
  if (dom) {
    // stop the recursion
    if (fn(dom) === false) return
    else {
      dom = dom.firstChild

      while (dom) {
        walk(dom, fn)
        dom = dom.nextSibling
      }
    }
  }
}

/**
 * Minimize risk: only zero or one _space_ between attr & value
 * @param   { String }   html - html string we want to parse
 * @param   { Function } fn - callback function to apply on any attribute found
 */
function walkAttributes(html, fn) {
  var m,
    re = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g

  while (m = re.exec(html)) {
    fn(m[1].toLowerCase(), m[2] || m[3] || m[4])
  }
}

/**
 * Check whether a DOM node is in stub mode, useful for the riot 'if' directive
 * @param   { Object }  dom - DOM node we want to parse
 * @returns { Boolean } -
 */
function isInStub(dom) {
  while (dom) {
    if (dom.inStub) return true
    dom = dom.parentNode
  }
  return false
}

/**
 * Create a generic DOM node
 * @param   { String } name - name of the DOM node we want to create
 * @returns { Object } DOM node just created
 */
function mkEl(name) {
  return document.createElement(name)
}

/**
 * Replace the yield tag from any tag template with the innerHTML of the
 * original tag in the page
 * @param   { String } tmpl - tag implementation template
 * @param   { String } innerHTML - original content of the tag in the DOM
 * @returns { String } tag template updated without the yield tag
 */
function replaceYield(tmpl, innerHTML) {
  return tmpl.replace(/<(yield)\/?>(<\/\1>)?/gi, innerHTML || '')
}

/**
 * Shorter and fast way to select multiple nodes in the DOM
 * @param   { String } selector - DOM selector
 * @param   { Object } ctx - DOM node where the targets of our search will is located
 * @returns { Object } dom nodes found
 */
function $$(selector, ctx) {
  return (ctx || document).querySelectorAll(selector)
}

/**
 * Shorter and fast way to select a single node in the DOM
 * @param   { String } selector - unique dom selector
 * @param   { Object } ctx - DOM node where the target of our search will is located
 * @returns { Object } dom node found
 */
function $(selector, ctx) {
  return (ctx || document).querySelector(selector)
}

/**
 * Simple object prototypal inheritance
 * @param   { Object } parent - parent object
 * @returns { Object } child instance
 */
function inherit(parent) {
  function Child() {}
  Child.prototype = parent
  return new Child()
}

/**
 * Get the name property needed to identify a DOM node in riot
 * @param   { Object } dom - DOM node we need to parse
 * @returns { String | undefined } give us back a string to identify this dom node
 */
function getNamedKey(dom) {
  return getAttr(dom, 'id') || getAttr(dom, 'name')
}

/**
 * Set the named properties of a tag element
 * @param { Object } dom - DOM node we need to parse
 * @param { Object } parent - tag instance where the named dom element will be eventually added
 * @param { Array } keys - list of all the tag instance properties
 */
function setNamed(dom, parent, keys) {
  // get the key value we want to add to the tag instance
  var key = getNamedKey(dom),
    // add the node detected to a tag instance using the named property
    add = function(value) {
      // avoid to override the tag properties already set
      if (contains(keys, key)) return
      // check whether this value is an array
      var isArr = isArray(value)
      // if the key was never set
      if (!value)
        // set it once on the tag instance
        parent[key] = dom
      // if it was an array and not yet set
      else if (!isArr || isArr && !contains(value, dom)) {
        // add the dom node into the array
        if (isArr)
          value.push(dom)
        else
          parent[key] = [value, dom]
      }
    }

  // skip the elements with no named properties
  if (!key) return

  // check whether this key has been already evaluated
  if (contains(key, brackets(0)))
    // wait the first updated event only once
    parent.one('updated', function() {
      key = getNamedKey(dom)
      add(parent[key])
    })
  else
    add(parent[key])

}

/**
 * Faster String startsWith alternative
 * @param   { String } src - source string
 * @param   { String } str - test string
 * @returns { Boolean } -
 */
function startsWith(src, str) {
  return src.slice(0, str.length) === str
}

/**
 * Function needed to inject in runtime the custom tags css
 */
var injectStyle = (function() {

  if (!window) return // skip injection on the server

  // create the style node
  var styleNode = mkEl('style'),
    placeholder = $('style[type=riot]'),
    head = document.head || document.getElementsByTagName('head')[0]

  setAttr(styleNode, 'type', 'text/css')

  // inject the new node into the DOM
  if (styleNode.styleSheet) {
    document.body.appendChild(styleNode)
  } else {
    if (placeholder) {
      placeholder.parentNode.insertBefore(styleNode, placeholder)
      placeholder.parentNode.removeChild(placeholder)
    } else head.appendChild(styleNode)
  }

  /**
   * This is the function exported that will be used to update the style tag just created
   * @param   { String } css [description]
   */
  return function(css) {
    if (styleNode.styleSheet)
      styleNode.styleSheet.cssText += css
    else
      styleNode.innerHTML += css
  }

})()

/**
 * Mount a tag creating new Tag instance
 * @param   { Object } root - dom node where the tag will be mounted
 * @param   { String } tagName - name of the riot tag we want to mount
 * @param   { Object } opts - options to pass to the Tag instance
 * @returns { Tag } a new Tag instance
 */
function mountTo(root, tagName, opts) {
  var tag = __tagImpl[tagName],
    // cache the inner HTML to fix #855
    innerHTML = root._innerHTML = root._innerHTML || root.innerHTML

  // clear the inner html
  root.innerHTML = ''

  if (tag && root) tag = new Tag(tag, { root: root, opts: opts }, innerHTML)

  if (tag && tag.mount) {
    tag.mount()
    // add this tag to the virtualDom variable
    if (!contains(__virtualDom, tag)) __virtualDom.push(tag)
  }

  return tag
}
<|endoftext|>


<commit_before>const clor = require("./")

console.log("" +
    clor("How").newLine("are").newLine("you?")
)

console.log(
    `${clor.red.bold("How").newLine.inverse("are").newLine.italic("you?")}`
)

console.log(
    clor.red.bold("How") + "\n" + clor.red.inverse("are") + "\n" + clor.red.italic("you?")
)

console.log("" +
    clor.bold(clor.red("Bold red")).blue(" and blue")
)

const Styles = {
    em: s => clor.bold.inverse(s)
}

console.log("" +
    clor(Styles.em("Known trope")).bold(" or meme")
)

// Tagged Literals

const c = require("./c")
const x = "Bold", y = "red", z = "blue"

console.log(
    c`<bold>${x}, <red>${y}</red></bold> and <blue>${z}.</blue>`
)

const name = ["Leia", "Han", "Ben"]
    [Math.floor(Math.random() * 4)]

console.log(
    c`Hi ${name ? c`<bold>${name}</bold>` : "everyone"}.`)<commit_msg>Remove example.<commit_after><|endoftext|>


<commit_before>var os = require('os')
var path = require('path')
var Mocha = require('mocha')
var assign = require('./lib/util/assign')
var klaw = require('klaw')

var argv = require('minimist')(process.argv.slice(2))

var mochaOpts = assign({
  ui: 'bdd',
  reporter: 'dot',
  timeout: 30000
}, argv)

var mocha = new Mocha(mochaOpts)

klaw('./lib').on('readable', function () {
  var item
  while ((item = this.read())) {
    if (!item.stats.isFile()) return
    if (item.path.lastIndexOf('.test.js') !== (item.path.length - '.test.js'.length)) return
    mocha.addFile(item.path)
  }
}).on('end', function () {
  mocha.run(function (failures) {
    require('./').remove(path.join(os.tmpdir(), 'fs-extra'), function () {
      process.exit(failures)
    })
  })
})
<commit_msg>Refactor test.js<commit_after>'use strict'

const os = require('os')
const path = require('path')
const klaw = require('klaw')
const Mocha = require('mocha')
const assign = require('./lib/util/assign')

const argv = require('minimist')(process.argv.slice(2))

const mochaOpts = assign({
  ui: 'bdd',
  reporter: 'dot',
  timeout: 30000
}, argv)

const mocha = new Mocha(mochaOpts)
const testExt = '.test.js'

klaw('./lib').on('readable', function () {
  let item
  while ((item = this.read())) {
    if (!item.stats.isFile()) return
    if (item.path.lastIndexOf(testExt) !== (item.path.length - testExt.length)) return
    mocha.addFile(item.path)
  }
}).on('end', () => {
  mocha.run(failures => {
    require('./').remove(path.join(os.tmpdir(), 'fs-extra'), () => process.exit(failures))
  })
})
<|endoftext|>


<commit_before>/*global describe, it, beforeEach */
'use strict';

import test from 'ava';
import gotZip from './';
import rimraf from 'rimraf';
import mkdirp from 'mkdirp';
import path from 'path';
import fs from 'fs';

const zip = 'https://github.com/ragingwind/got-zip/archive/v0.2.2.zip';
var opts = {
	dest: path.join(__dirname, './.tmp')
};
var filename = path.join(opts.dest, path.basename(zip));

test.beforeEach(function() {
	rimraf.sync(opts.dest);
	mkdirp(opts.dest);
});

test.serial('should download, extact and cleanup a zip', t => {
	return gotZip(zip, opts).then(() => {
		t.ok(!fs.existsSync(filename));
	});
});

test.serial('should exist a zip', t => {
	opts.cleanup = false;

	return gotZip(zip, opts).then(() => {
		t.ok(fs.existsSync(filename));
	});
});

test.serial('should exclude all of the files', t => {
	opts.exclude = [
		'package.json',
		'readme.md'
	];

	return gotZip(zip, opts).then(() => {
		t.ok(!fs.existsSync(path.join(opts.dest, 'package.json')));
		t.ok(!fs.existsSync(path.join(opts.dest, 'readme.md')));
	});
});
<commit_msg>Using existsSync<commit_after>/*global describe, it, beforeEach */
'use strict';

import test from 'ava';
import gotZip from './';
import rimraf from 'rimraf';
import mkdirp from 'mkdirp';
import path from 'path';
import fs from 'fs';

const zip = 'https://github.com/ragingwind/got-zip/archive/v0.2.2.zip';
const opts = {
	dest: path.join(__dirname, './.tmp')
};
const existsSync = function (filename) {
	return fs.existsSync(path.join(opts.dest, filename));
}

test.beforeEach(function () {
	rimraf.sync(opts.dest);
	mkdirp(opts.dest);
});

test.serial('should download, extact and cleanup a zip', t => {
	return gotZip(zip, opts).then(() => {
		t.ok(!existsSync(path.basename(zip)));
	});
});

test.serial('should exist a zip', t => {
	opts.cleanup = false;

	return gotZip(zip, opts).then(() => {
		t.ok(existsSync(path.basename(zip)));
	});
});

test.serial('should exclude all of the files', t => {
	opts.exclude = [
		'package.json',
		'readme.md'
	];

	return gotZip(zip, opts).then(() => {
		t.ok(!existsSync('package.json'));
		t.ok(!existsSync('readme.md'));
	});
});
<|endoftext|>