<commit_before>import { bindable, customAttribute } from 'aurelia-templating';
import { bindingMode } from 'aurelia-binding';
import { inject } from 'aurelia-dependency-injection';
import { AttributeManager } from '../common/attributeManager';
import { getBooleanFromAttributeValue } from '../common/attributes';

@customAttribute('md-dropdown')
@inject(Element)
export class MdDropdown {
  static elementId = 0;

  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) activates = '';
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) ref = null;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) alignment = 'left';
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) belowOrigin = false;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) constrainWidth = true;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) gutter = 0;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) hover = false;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) mdTitle;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) inDuration = 300;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) outDuration = 225;
  @bindable({
      defaultBindingMode: bindingMode.oneTime
  }) stopPropagation = false;

  constructor(element) {
    this.element = element;
    this.attributeManager = new AttributeManager(this.element);
  }

  attached() {
    this.handleActivateElement();
    this.contentAttributeManager = new AttributeManager(document.getElementById(this.activates));

    this.attributeManager.addClasses('dropdown-button');
    this.contentAttributeManager.addClasses('dropdown-content');
    // this.attributeManager.addAttributes({ 'data-activates': this.activates });

    $(this.element).dropdown({
      alignment: this.alignment,
      belowOrigin: getBooleanFromAttributeValue(this.belowOrigin),
      constrain_width: getBooleanFromAttributeValue(this.constrainWidth),
      constrainWidth: getBooleanFromAttributeValue(this.constrainWidth),
      gutter: parseInt(this.gutter, 10),
      hover: getBooleanFromAttributeValue(this.hover),
      inDuration: parseInt(this.inDuration, 10),
      outDuration: parseInt(this.outDuration, 10),
      stopPropagation: getBooleanFromAttributeValue(this.stopPropagation)
    });
  }

  detached() {
    this.attributeManager.removeAttributes('data-activates');
    this.attributeManager.removeClasses('dropdown-button');
    this.contentAttributeManager.removeClasses('dropdown-content');
  }

  handleActivateElement() {
    if (this.ref) {
      let id = this.ref.getAttribute('id');
      if (!id) {
        id = `md-dropdown-${MdDropdown.elementId++}`;
        this.ref.setAttribute('id', id);
        this.activates = id;
      }
      this.id = MdDropdown.elementId++;
    }
    this.attributeManager.addAttributes({ 'data-activates': this.activates });
  }
}
<commit_msg>feat(md-dropdown): add open/close methods<commit_after>import { bindable, customAttribute } from 'aurelia-templating';
import { bindingMode } from 'aurelia-binding';
import { inject } from 'aurelia-dependency-injection';
import { AttributeManager } from '../common/attributeManager';
import { getBooleanFromAttributeValue } from '../common/attributes';

@customAttribute('md-dropdown')
@inject(Element)
export class MdDropdown {
  static elementId = 0;

  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) activates = '';
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) ref = null;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) alignment = 'left';
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) belowOrigin = false;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) constrainWidth = true;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) gutter = 0;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) hover = false;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) mdTitle;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) inDuration = 300;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) outDuration = 225;
  @bindable({
    defaultBindingMode: bindingMode.oneTime
  }) stopPropagation = false;

  constructor(element) {
    this.element = element;
    this.attributeManager = new AttributeManager(this.element);
  }

  attached() {
    this.handleActivateElement();
    this.contentAttributeManager = new AttributeManager(document.getElementById(this.activates));

    this.attributeManager.addClasses('dropdown-button');
    this.contentAttributeManager.addClasses('dropdown-content');
    // this.attributeManager.addAttributes({ 'data-activates': this.activates });

    $(this.element).dropdown({
      alignment: this.alignment,
      belowOrigin: getBooleanFromAttributeValue(this.belowOrigin),
      constrain_width: getBooleanFromAttributeValue(this.constrainWidth),
      constrainWidth: getBooleanFromAttributeValue(this.constrainWidth),
      gutter: parseInt(this.gutter, 10),
      hover: getBooleanFromAttributeValue(this.hover),
      inDuration: parseInt(this.inDuration, 10),
      outDuration: parseInt(this.outDuration, 10),
      stopPropagation: getBooleanFromAttributeValue(this.stopPropagation)
    });
  }

  detached() {
    this.attributeManager.removeAttributes('data-activates');
    this.attributeManager.removeClasses('dropdown-button');
    this.contentAttributeManager.removeClasses('dropdown-content');
  }

  open() {
    $(this.element).dropdown('open');
  }

  close() {
    $(this.element).dropdown('close');
  }

  handleActivateElement() {
    if (this.ref) {
      let id = this.ref.getAttribute('id');
      if (!id) {
        id = `md-dropdown-${MdDropdown.elementId++}`;
        this.ref.setAttribute('id', id);
        this.activates = id;
      }
      this.id = MdDropdown.elementId++;
    }
    this.attributeManager.addAttributes({ 'data-activates': this.activates });
  }
}
<|endoftext|>


<commit_before>import React from 'react'

class Loading extends React.Component {

  constructor(props) {

    super(props)

    this.state = { text: "loading..." }
  }

  componentDidMount() {

    let that = this

    this.interval = window.setInterval( ()=> that.setState({text: this.state.text+"."}), 10)
  }

  componentWillUnMount() {

    window.clearInterval(this.interval)
  }

  render() {

    return
{this.state.text}

  }

}

export default Loading
<commit_msg>page.js au lieu de react-router<commit_after>import React from 'react'

class Loading extends React.Component {

  constructor(props) {

    super(props)

    this.state = { text: "loading..." }
  }

  componentDidMount() {

    let that = this

    this.interval = window.setInterval( ()=> that.setState({text: this.state.text+"."}), 10)
  }

  componentWillUnmount() {

    window.clearInterval(this.interval)
  }

  render() {

    return
{this.state.text}

  }

}

export default Loading
<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular
    .module('fusionSeedApp.components.typeahead')
    .service('TypeaheadService', TypeaheadService);

  TypeaheadService.$inject = ['$log', '$http', '$q', 'ConfigService', 'ApiBase', 'QueryBuilder'];
  function TypeaheadService($log, $http, $q, ConfigService, ApiBase, QueryBuilder){
    var endpoint = ApiBase.getEndpoint();
    var collectionName = ConfigService.getCollectionName();
    var requestHandler = ConfigService.getTypeaheadRequestHandler();
    var profile = ConfigService.getTypeaheadProfile();
    var pipeline = ConfigService.getTypeaheadPipeline();

    return {
      getQueryResults: getQueryResults
    };

    function getQueryResults(query){
      var deferred = $q.defer();
      var queryString = QueryBuilder.objectToURLString(query);
      var fullUrl = getQueryUrl(ConfigService.getIfQueryProfile()) + '?' + queryString;

      $http
        .get(fullUrl)
        .then(success)
        .catch(failure);

      function success(response) {
        // Set the content to populate the rest of the ui.
        deferred.resolve(response.data);
      }

      function failure(err) {
        deferred.reject(err.data);
      }

      return deferred.promise;
    }

    /**
     * Private function
     */
    function getQueryUrl(isProfile){
      var profileUrl = endpoint +
        'api/apollo/collections/' +
        collectionName +
        '/query-profiles/' +
        profile + '/' +
        requestHandler;

      var pipelineUrl = endpoint +
        'api/apollo/query-pipelines/' +
        pipeline +
        '/collection/' +
        collectionName + '/' +
        requestHandler;

      return isProfile?profileUrl:pipelineUrl;
    }

  }
})();
<commit_msg>service to factory<commit_after>(function () {
  'use strict';

  angular
    .module('fusionSeedApp.components.typeahead')
    .factory('TypeaheadService', TypeaheadService);

  TypeaheadService.$inject = ['$log', '$http', '$q', 'ConfigService', 'ApiBase', 'QueryBuilder'];
  function TypeaheadService($log, $http, $q, ConfigService, ApiBase, QueryBuilder){
    var endpoint = ApiBase.getEndpoint();
    var collectionName = ConfigService.getCollectionName();
    var requestHandler = ConfigService.getTypeaheadRequestHandler();
    var profile = ConfigService.getTypeaheadProfile();
    var pipeline = ConfigService.getTypeaheadPipeline();

    return {
      getQueryResults: getQueryResults
    };

    function getQueryResults(query){
      var deferred = $q.defer();
      var queryString = QueryBuilder.objectToURLString(query);
      var fullUrl = getQueryUrl(ConfigService.getIfQueryProfile()) + '?' + queryString;

      $http
        .get(fullUrl)
        .then(success)
        .catch(failure);

      function success(response) {
        // Set the content to populate the rest of the ui.
        deferred.resolve(response.data);
      }

      function failure(err) {
        deferred.reject(err.data);
      }

      return deferred.promise;
    }

    /**
     * Private function
     */
    function getQueryUrl(isProfile){
      var profileUrl = endpoint +
        'api/apollo/collections/' +
        collectionName +
        '/query-profiles/' +
        profile + '/' +
        requestHandler;

      var pipelineUrl = endpoint +
        'api/apollo/query-pipelines/' +
        pipeline +
        '/collection/' +
        collectionName + '/' +
        requestHandler;

      return isProfile?profileUrl:pipelineUrl;
    }

  }
})();
<|endoftext|>


<commit_before>if (typeof SharedArrayBuffer !== 'function' || typeof Atomics !== 'object') {
    document.getElementById('output').textContent = 'This browser does not support SharedArrayBuffers';
    return;
}

const worker = new Worker('worker.js');
worker.addEventListener('message', function (event) {
    document.getElementById('output').textContent = event.data;
});

const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
const sharedArray = new Int32Array(sharedBuffer);
Lock.initialize(sharedArray, 0);
const lock = new Lock(sharedArray, 0);
lock.lock();
document.getElementById('unlock').addEventListener('click', event => {
    event.preventDefault();
    lock.unlock();
});

// Old API: transfer
worker.postMessage({sharedBuffer}, [sharedBuffer]);

// New API: clone
// worker.postMessage({sharedBuffer});
<commit_msg>Wrap script in IIFE<commit_after>(function () { // BEGIN iife

if (typeof SharedArrayBuffer !== 'function' || typeof Atomics !== 'object') {
    document.getElementById('output').textContent = 'This browser does not support SharedArrayBuffers';
    return;
}

const worker = new Worker('worker.js');
worker.addEventListener('message', function (event) {
    document.getElementById('output').textContent = event.data;
});

const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
const sharedArray = new Int32Array(sharedBuffer);
Lock.initialize(sharedArray, 0);
const lock = new Lock(sharedArray, 0);
lock.lock();
document.getElementById('unlock').addEventListener('click', event => {
    event.preventDefault();
    lock.unlock();
});

// Old API: transfer
worker.postMessage({sharedBuffer}, [sharedBuffer]);

// New API: clone
// worker.postMessage({sharedBuffer});

})(); // END iife
<|endoftext|>


<commit_before>import React from 'react'
import Avatar from './Avatar'
import { humanDate, present } from '../util/text'
import { sanitize } from 'hylo-utils/text'
var { func, object } = React.PropTypes

class Message extends React.Component {
  static propTypes = {
    message: object.isRequired
  }

  static contextTypes = {
    dispatch: func.isRequired,
    currentUser: object
  }

  render () {
    const { message } = this.props

    const person = message.user
    let text = present(sanitize(message.text))

    return

      <a name={`message-${message.id}`}>
      <Avatar person={person}/>

          {sanitize(person.name)}
          {humanDate(message.created_at)}

  }
}

export default Message
<commit_msg>simple support for newlines in comments<commit_after>import React from 'react'
import Avatar from './Avatar'
import { humanDate, present } from '../util/text'
import { sanitize } from 'hylo-utils/text'
var { func, object } = React.PropTypes

class Message extends React.Component {
  static propTypes = {
    message: object.isRequired
  }

  static contextTypes = {
    dispatch: func.isRequired,
    currentUser: object
  }

  render () {
    const { message } = this.props

    const person = message.user
    let text = present(sanitize(message.text)).replace(/\n/, '
')

    return

      <a name={`message-${message.id}`}>
      <Avatar person={person}/>

          {sanitize(person.name)}
          {humanDate(message.created_at)}

  }
}

export default Message
<|endoftext|>


<commit_before>
/*
 * Appcelerator Titanium Mobile
 * Copyright (c) 2011-2015 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */
var should = require('./should');

describe("Titanium.UI.AttributedString", function () {
    it("attributedString", function (finish) {
var win = Titanium.UI.createWindow({
  backgroundColor: '#ddd',
});
var string = Ti.UI.createAttributedString({
    text: 'Appcelerator always blue and underlined',
    attributes: [
      {
        type: Ti.UI.ATTRIBUTE_LINK,
        value: 'http://www.appcelerator.com',
        range: [0, 12]
      },
      {
        type: Ti.UI.ATTRIBUTE_FOREGROUND_COLOR,
        value: 'red',
        range: [0, 12]
      },
      {
        type: Ti.UI.iOS.ATTRIBUTE_UNDERLINES_STYLE,
        value: Ti.UI.iOS.ATTRIBUTE_UNDERLINE_STYLE_NONE,
        range: [0, 12]
      },
    {
        type: Ti.UI.ATTRIBUTE_LINK,
        value: 'https://www.youtube.com',
        range: [20, 4]
      }]
  });

var label = Ti.UI.createLabel({
  top: 100,
  attributedString: string
});
    win.add(label);
    win.open();

    label.addEventListener("link",function(e)
    {
      var webview = Titanium.UI.createWebView({
        url:e.url,
        height: 300,
        width:  300
       });
      win.add(webview);
    });

var attributes = [];
attributes = string.getAttributes();
should(attributes[0].value).eql("http://www.appcelerator.com");
should(attributes[1].value).eql("red");
should(attributes[3].value).eql("https://www.youtube.com");
finish();
    });

    });<commit_msg>[TIMOB-19165] iOS: Mocha Fix<commit_after>
/*
 * Appcelerator Titanium Mobile
 * Copyright (c) 2011-2015 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */
var should = require('./should');

describe("Titanium.UI.AttributedString", function () {
    it("attributedString", function (finish) {
var win = Titanium.UI.createWindow({
  backgroundColor: '#ddd',
});
var string = Ti.UI.createAttributedString({
    text: 'Appcelerator always blue and underlined',
    attributes: [
      {
        type: Ti.UI.ATTRIBUTE_LINK,
        value: 'http://www.appcelerator.com',
        range: [0, 12]
      },
      {
        type: Ti.UI.ATTRIBUTE_FOREGROUND_COLOR,
        value: 'red',
        range: [0, 12]
      },
      {
        type: Ti.UI.ATTRIBUTE_UNDERLINES_STYLE,
        value: Ti.UI.ATTRIBUTE_UNDERLINE_STYLE_NONE,
        range: [0, 12]
      },
    {
        type: Ti.UI.ATTRIBUTE_LINK,
        value: 'https://www.youtube.com',
        range: [20, 4]
      }]
  });

var label = Ti.UI.createLabel({
  top: 100,
  attributedString: string
});
    win.add(label);
    win.open();

    label.addEventListener("link",function(e)
    {
      var webview = Titanium.UI.createWebView({
        url:e.url,
        height: 300,
        width:  300
       });
      win.add(webview);
    });

var attributes = [];
attributes = string.getAttributes();
should(attributes[0].value).eql("http://www.appcelerator.com");
should(attributes[1].value).eql("red");
should(attributes[3].value).eql("https://www.youtube.com");
finish();
    });

    });<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');

var appConfig = {
  environment: function() {
    var environment = {
      name: process.env.NODE_ENV,
    };

    return environment;
  },

  web: function() {
    var web = {
      https: {
        port: process.env.DNB_N_NODEJS_OAI_HTTPS_PORT,
        crt: process.env.DNB_ENV_V_SEC_PATH + '/' + process.env.DNB_ENV_APP_S1_WB_FTED_HTTPS_CERT1_PUB,
        key: process.env.DNB_ENV_V_SEC_PATH + '/' + process.env.DNB_ENV_APP_S1_WB_FTED_HTTPS_CERT1_KEY,
      },
      // not an ip ... maybe better hops .. change nginx ip settings (show real client ip)
      // proxies: [ process.env.DNB_INF1_ENV_S1_WF ],
    };

    return web;
  },
};

(function readPKIFiles() {
  process.env.CYPHERME_WEB_HTTPS_KEY = fs.readFileSync(appConfig.web().https.key, 'utf8');
  process.env.CYPHERME_WEB_HTTPS_CRT = fs.readFileSync(appConfig.web().https.crt, 'utf8');
})();

module.exports = appConfig;
<commit_msg>test commit for new build arg repo head<commit_after>'use strict';

var fs = require('fs');

var appConfig = {
  environment: function() {
    var environment = {
      name: process.env.NODE_ENV,
    };

    return environment;
  },

  web: function() {
    var web = {
      https: {
        port: process.env.DNB_N_NODEJS_OAI_HTTPS_PORT,
        crt: process.env.DNB_ENV_V_SEC_PATH + '/' + process.env.DNB_ENV_APP_S1_WB_FTED_HTTPS_CERT1_PUB,
        key: process.env.DNB_ENV_V_SEC_PATH + '/' + process.env.DNB_ENV_APP_S1_WB_FTED_HTTPS_CERT1_KEY,
      },
      // not an ip ... maybe better hops .. change nginx ip settings (show real client ip)
      // proxies: [ process.env.DNB_INF1_ENV_S1_WF ],
    };

    return web;
  },
};

(function readPKIFiles() {
  process.env.CYPHERME_WEB_HTTPS_KEY = fs.readFileSync(appConfig.web().https.key, 'utf8');
  process.env.CYPHERME_WEB_HTTPS_CRT = fs.readFileSync(appConfig.web().https.crt, 'utf8');
})();

module.exports = appConfig;

<|endoftext|>


<commit_before>import React from 'react'
import ReactDOM from 'react-dom'

// import ProfilePhoto from './ProfilePhoto'
import ProfileName from './ProfileName'
import ProgressBar from './ProgressBar'
import DonateForm from './DonateForm'
import SobStory from './SobStory'

export default React.createClass({

    getInitialState: function () {
    return {
      amountDonated: 0
    }
  },

  handleDonation: function (p) {
  console.log("hey look this is being passed up!", p, "type", typeof p)
  this.setState({amountDonated: p})
  },

  render: function () {
    const { recipientID } = this.props.params

    return (

        <ProfileName name={recipientID}/>
        <ProgressBar/>
        <DonateForm donateFunction={this.handleDonation.bind(this)} />
        <SobStory sobstory='test sob story' />

    )
  }
})
<commit_msg>Add updated /recipients page<commit_after>import React from 'react'
import ReactDOM from 'react-dom'

// import ProfilePhoto from './ProfilePhoto'
import NavBar from './NavBar'
import Header from './Header'
import ProfileName from './ProfileName'
import ProgressBar from './ProgressBar'
import DonateForm from './DonateForm'
import SobStory from './SobStory'

export default React.createClass({

    getInitialState: function () {
    return {
      amountDonated: 0
    }
  },

  handleDonation: function (p) {
  console.log("hey look this is being passed up!", p, "type", typeof p)
  this.setState({amountDonated: p})
  },

  render: function () {
    const { recipientID } = this.props.params

    return (

        <NavBar/>

        <ProfileName name='Richard'/>
        <ProgressBar/>
        <DonateForm donateFunction={this.handleDonation.bind(this)} />
        <SobStory sobstory='test sob story' />

    )
  }
})
<|endoftext|>


<commit_before>import React from 'react'
import { Button, Col, ControlLabel, FormControl, FormGroup, Row, HelpBlock, Checkbox } from 'react-bootstrap'
import Icon from 'react-fontawesome'
import { connect } from 'react-redux'

import * as actions from '../actions'
import * as selectors from '../selectors'
import * as utils from '../utils'

const mapStateToProps = (state, props) => {
  const event = selectors.getEvent(state, props.eventUrl)
  return ({
    totalAmount:  selectors.getTotalAmountForEvent(state, props.eventUrl),
    user: selectors.getCurrentUser(state),
    organization: selectors.getOrganization(state, event.get('organization'))
  })
}

const mapDispatchToProps = (dispatch, props) => ({
  performPurchase: (stripeToken, nin) => dispatch(actions.performPurchase(props.eventUrl, stripeToken, nin))
})

class _PaymentForm extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      cardNumber: '',
      cardExpMonth: '',
      cardExpYear: '',
      cardCode: '',
      nin: props.user.nin || '',
      noNin: false,
      stripeError: null,
      stripePending: null
    }
  }

  toggleNoNin() {
    return (domEvent) => this.setState({noNin: !this.state.noNin})
  }

  setCardNumber() {
    return (domEvent) => this.setState(
      // Replaces all spaces with blank strings
      {cardNumber: domEvent.target.value.replace(/\D/g, '')})
  }

  setCardExpMonth() {
    return (domEvent) => this.setState(
      {cardExpMonth: domEvent.target.value.replace(/\D/g, '').slice(0, 2)})
  }

  setCardExpYear() {
  return (domEvent) => this.setState(
    {cardExpYear: domEvent.target.value.replace(/\D/g, '').slice(0, 4)})
  }

  setCardCode() {
    return (domEvent) => this.setState(
      {cardCode: domEvent.target.value.replace(/\D/g, '').slice(0, 4)})
  }

  setNin() {
    return (domEvent) => this.setState({nin: domEvent.target.value})
  }

  cardCodeIsValid() {
    return window.Stripe.card.validateCVC(this.state.cardCode)
  }

  cardExpIsValid() {
    return window.Stripe.card.validateExpiry(
      this.state.cardExpMonth, this.state.cardExpYear)
  }

  cardNumberIsValid() {
    return window.Stripe.card.validateCardNumber(this.state.cardNumber)
  }

  ninIsValid() {
    return this.state.noNin || utils.ninIsValid(this.state.nin)
  }

  stripeResponseHandler(status, response) {
    if (response.error) {
      this.setState({stripeError: response.error})
      this.setState({stripePending: false})
    } else {
      this.setState({stripeError: null})

      this.props.performPurchase(response.id, this.state.noNin ? null : this.state.nin)
      this.setState({stripePending: false})
    }
  }

  startPurchase() {
    return (domEvent) => {
      this.setState({stripeError: null})
      this.setState({stripePending: true})

      window.Stripe.createToken({
        number: this.state.cardNumber,
        cvc: this.state.cardCode,
        exp_month: this.state.cardExpMonth,
        exp_year: this.state.cardExpYear
      }, this.stripeResponseHandler.bind(this))

      domEvent.preventDefault()
      return false
    }
  }

  render() {
    return (
      <form onSubmit={this.startPurchase()}>

Personal details

        <Row>
          <Col sm={6} md={12}>
            <FormGroup>
              <ControlLabel>Name</ControlLabel>
              <FormControl type="text" disabled value={this.props.user.name}/>
            </FormGroup>
          </Col>
          <Col sm={6} md={12}>
            <FormGroup>
              <ControlLabel>E-mail address</ControlLabel>
              <FormControl type="email" disabled value={this.props.user.email}/>
              <HelpBlock>
                All correspondence (such as receipts and confirmations) will be sent
                to this address.
              </HelpBlock>
            </FormGroup>
          </Col>
        </Row>

        <FormGroup validationState={(this.state.nin && !this.ninIsValid()) ? 'error' : null}>
          <ControlLabel>National identity number</ControlLabel>
          <FormControl type="text" placeholder={this.state.noNin ? '—' : 'YYYYMMDDXXXX'} value={this.state.noNin ? '' : this.state.nin} onChange={this.setNin()} disabled={this.state.noNin}/>
          <HelpBlock>
            Swedish personnummer. Used to identify you at the event. Make sure this is correct!
          </HelpBlock>
          <Checkbox checked={this.state.noNin} onChange={this.toggleNoNin()}>
            I don't have a Swedish national identity number
          </Checkbox>
        </FormGroup>

Card details

          We accept VISA, MasterCard and American Express cards.

        <FormGroup validationState={(this.state.cardNumber && !this.cardNumberIsValid()) ? 'error' : null}>
          <ControlLabel>Card number</ControlLabel>
          <FormControl type="text" placeholder="●●●● ●●●● ●●●● ●●●●" value={this.state.cardNumber.replace(/\d{4}/g, '$& ').trim()} onChange={this.setCardNumber()}/>
        </FormGroup>
        <Row>
          <Col xs={3}>
            <FormGroup validationState={(this.state.cardExpMonth && this.state.cardExpYear && !this.cardExpIsValid()) ? 'error' : null}>
              <ControlLabel>Month</ControlLabel>
              <FormControl type="text" placeholder="MM" value={this.state.cardExpMonth} onChange={this.setCardExpMonth()}/>
            </FormGroup>
          </Col>
          <Col xs={3}>
            <FormGroup validationState={(this.state.cardExpMonth && this.state.cardExpYear && !this.cardExpIsValid()) ? 'error' : null}>
              <ControlLabel>Year</ControlLabel>
              <FormControl type="text" placeholder="YY" value={this.state.cardExpYear} onChange={this.setCardExpYear()}/>
            </FormGroup>
          </Col>
          <Col xs={6}>
            <FormGroup validationState={(this.state.cardCode && !this.cardCodeIsValid()) ? 'error' : null}>
              <ControlLabel>Security code</ControlLabel>
              <FormControl type="text" placeholder="●●●" value={this.state.cardCode} onChange={this.setCardCode()}/>
            </FormGroup>
          </Col>
        </Row>

Terms and other important stuff

          Your tickets will be delivered electronically to your profile on this
          website within a few seconds from your purchase.

          Your payment is handled securely by Stripe. The amount will be
          withdrawn immediately from the card supplied above.

          Your purchase from {this.props.organization.get('name')} is protected
          by applicable Swedish law, which (in conjunction with Bitket's terms)
          has the following important implications:

            Once your tickets has been delivered to you, you cannot
            withdraw your purchase unless it is resold to another
            person.

            Your tickets are personal, meaning that you may have to identify
            yourself using valid passport, national identification card, driver's
            license or student identification card in order to utilize the tickets.

          By paying, you agree to these terms.

        <FormGroup>
          <Button type="submit" bsSize="lg" bsStyle="success" block
                  disabled={!(this.cardCodeIsValid() && this.cardExpIsValid() && this.cardNumberIsValid() && this.ninIsValid()) || this.state.stripePending}>
            Pay {this.props.totalAmount.toRepr()} SEK
          </Button>
        </FormGroup>

      </form>
    )
  }
}

const PaymentForm = connect(mapStateToProps, mapDispatchToProps)(_PaymentForm)

export default PaymentForm
<commit_msg>Added fallback on organization name<commit_after>import React from 'react'
import { Button, Col, ControlLabel, FormControl, FormGroup, Row, HelpBlock, Checkbox } from 'react-bootstrap'
import Icon from 'react-fontawesome'
import { connect } from 'react-redux'

import * as actions from '../actions'
import * as selectors from '../selectors'
import * as utils from '../utils'

const mapStateToProps = (state, props) => {
  const event = selectors.getEvent(state, props.eventUrl)
  return ({
    totalAmount:  selectors.getTotalAmountForEvent(state, props.eventUrl),
    user: selectors.getCurrentUser(state),
    organization: selectors.getOrganization(state, event.get('organization'))
  })
}

const mapDispatchToProps = (dispatch, props) => ({
  performPurchase: (stripeToken, nin) => dispatch(actions.performPurchase(props.eventUrl, stripeToken, nin))
})

class _PaymentForm extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      cardNumber: '',
      cardExpMonth: '',
      cardExpYear: '',
      cardCode: '',
      nin: props.user.nin || '',
      noNin: false,
      stripeError: null,
      stripePending: null
    }
  }

  toggleNoNin() {
    return (domEvent) => this.setState({noNin: !this.state.noNin})
  }

  setCardNumber() {
    return (domEvent) => this.setState(
      // Replaces all spaces with blank strings
      {cardNumber: domEvent.target.value.replace(/\D/g, '')})
  }

  setCardExpMonth() {
    return (domEvent) => this.setState(
      {cardExpMonth: domEvent.target.value.replace(/\D/g, '').slice(0, 2)})
  }

  setCardExpYear() {
  return (domEvent) => this.setState(
    {cardExpYear: domEvent.target.value.replace(/\D/g, '').slice(0, 4)})
  }

  setCardCode() {
    return (domEvent) => this.setState(
      {cardCode: domEvent.target.value.replace(/\D/g, '').slice(0, 4)})
  }

  setNin() {
    return (domEvent) => this.setState({nin: domEvent.target.value})
  }

  cardCodeIsValid() {
    return window.Stripe.card.validateCVC(this.state.cardCode)
  }

  cardExpIsValid() {
    return window.Stripe.card.validateExpiry(
      this.state.cardExpMonth, this.state.cardExpYear)
  }

  cardNumberIsValid() {
    return window.Stripe.card.validateCardNumber(this.state.cardNumber)
  }

  ninIsValid() {
    return this.state.noNin || utils.ninIsValid(this.state.nin)
  }

  stripeResponseHandler(status, response) {
    if (response.error) {
      this.setState({stripeError: response.error})
      this.setState({stripePending: false})
    } else {
      this.setState({stripeError: null})

      this.props.performPurchase(response.id, this.state.noNin ? null : this.state.nin)
      this.setState({stripePending: false})
    }
  }

  startPurchase() {
    return (domEvent) => {
      this.setState({stripeError: null})
      this.setState({stripePending: true})

      window.Stripe.createToken({
        number: this.state.cardNumber,
        cvc: this.state.cardCode,
        exp_month: this.state.cardExpMonth,
        exp_year: this.state.cardExpYear
      }, this.stripeResponseHandler.bind(this))

      domEvent.preventDefault()
      return false
    }
  }

  render() {
    return (
      <form onSubmit={this.startPurchase()}>

Personal details

        <Row>
          <Col sm={6} md={12}>
            <FormGroup>
              <ControlLabel>Name</ControlLabel>
              <FormControl type="text" disabled value={this.props.user.name}/>
            </FormGroup>
          </Col>
          <Col sm={6} md={12}>
            <FormGroup>
              <ControlLabel>E-mail address</ControlLabel>
              <FormControl type="email" disabled value={this.props.user.email}/>
              <HelpBlock>
                All correspondence (such as receipts and confirmations) will be sent
                to this address.
              </HelpBlock>
            </FormGroup>
          </Col>
        </Row>

        <FormGroup validationState={(this.state.nin && !this.ninIsValid()) ? 'error' : null}>
          <ControlLabel>National identity number</ControlLabel>
          <FormControl type="text" placeholder={this.state.noNin ? '—' : 'YYYYMMDDXXXX'} value={this.state.noNin ? '' : this.state.nin} onChange={this.setNin()} disabled={this.state.noNin}/>
          <HelpBlock>
            Swedish personnummer. Used to identify you at the event. Make sure this is correct!
          </HelpBlock>
          <Checkbox checked={this.state.noNin} onChange={this.toggleNoNin()}>
            I don't have a Swedish national identity number
          </Checkbox>
        </FormGroup>

Card details

          We accept VISA, MasterCard and American Express cards.

        <FormGroup validationState={(this.state.cardNumber && !this.cardNumberIsValid()) ? 'error' : null}>
          <ControlLabel>Card number</ControlLabel>
          <FormControl type="text" placeholder="●●●● ●●●● ●●●● ●●●●" value={this.state.cardNumber.replace(/\d{4}/g, '$& ').trim()} onChange={this.setCardNumber()}/>
        </FormGroup>
        <Row>
          <Col xs={3}>
            <FormGroup validationState={(this.state.cardExpMonth && this.state.cardExpYear && !this.cardExpIsValid()) ? 'error' : null}>
              <ControlLabel>Month</ControlLabel>
              <FormControl type="text" placeholder="MM" value={this.state.cardExpMonth} onChange={this.setCardExpMonth()}/>
            </FormGroup>
          </Col>
          <Col xs={3}>
            <FormGroup validationState={(this.state.cardExpMonth && this.state.cardExpYear && !this.cardExpIsValid()) ? 'error' : null}>
              <ControlLabel>Year</ControlLabel>
              <FormControl type="text" placeholder="YY" value={this.state.cardExpYear} onChange={this.setCardExpYear()}/>
            </FormGroup>
          </Col>
          <Col xs={6}>
            <FormGroup validationState={(this.state.cardCode && !this.cardCodeIsValid()) ? 'error' : null}>
              <ControlLabel>Security code</ControlLabel>
              <FormControl type="text" placeholder="●●●" value={this.state.cardCode} onChange={this.setCardCode()}/>
            </FormGroup>
          </Col>
        </Row>

Terms and other important stuff

          Your tickets will be delivered electronically to your profile on this
          website within a few seconds from your purchase.

          Your payment is handled securely by Stripe. The amount will be
          withdrawn immediately from the card supplied above.

          Your purchase from {this.props.organization ? this.props.organization.get('name') : 'the event organizer'} is protected
          by applicable Swedish law, which (in conjunction with Bitket's terms)
          has the following important implications:

            Once your tickets has been delivered to you, you cannot
            withdraw your purchase unless it is resold to another
            person.

            Your tickets are personal, meaning that you may have to identify
            yourself using valid passport, national identification card, driver's
            license or student identification card in order to utilize the tickets.

          By paying, you agree to these terms.

        <FormGroup>
          <Button type="submit" bsSize="lg" bsStyle="success" block
                  disabled={!(this.cardCodeIsValid() && this.cardExpIsValid() && this.cardNumberIsValid() && this.ninIsValid()) || this.state.stripePending}>
            Pay {this.props.totalAmount.toRepr()} SEK
          </Button>
        </FormGroup>

      </form>
    )
  }
}

const PaymentForm = connect(mapStateToProps, mapDispatchToProps)(_PaymentForm)

export default PaymentForm
<|endoftext|>


<commit_before>define(function (require, exports, module) {
    "use strict";

    /** --- MODULES --- **/
    var CommandManager = brackets.getModule("command/CommandManager"),
        Menus = brackets.getModule("command/Menus"),
        DocumentManager = brackets.getModule("document/DocumentManager"),
        EditorManager = brackets.getModule("editor/EditorManager"),
        ExtensionUtils = brackets.getModule("utils/ExtensionUtils"),
        NodeConnection = brackets.getModule("utils/NodeConnection"),
        Dialogs = brackets.getModule("widgets/Dialogs"),
        ansi = require("ansi"),
        nodeConnection = new NodeConnection(),
        NodeMenuID = "node-menu",
        NodeMenu = Menus.addMenu("Node.js", NodeMenuID),
        source = null,
        NODE_SETTINGS_DIALOG_ID = "node-settings-dialog",
        NODE_INSTALL_DIALOG_ID = "node-install-dialog",
        NODE_EXEC_DIALOG_ID = "node-exec-dialog",
        LS_PREFIX = "node-",
        API_VERSION = 1;

    /**
     * Shortcuts for localstorage with prefix
     */
    function get(name) {
        return localStorage.getItem(LS_PREFIX + name);
    }

    function set(name, value) {
        return localStorage.setItem(LS_PREFIX + name, value);
    }

    function rm(name) {
        return localStorage.removeItem(LS_PREFIX + name);
    }

    /**
     * Load the configuration
     */
    var config = JSON.parse(require("text!config.json"));

    /**
     * Start the node server
     */
    nodeConnection.connect(true).then(function () {
        nodeConnection.loadDomains(
            [ExtensionUtils.getModulePath(module, "server.js")],
            true
        ).then(
            function () {
                console.log("[brackets-nodejs] Connected to nodejs");
            }
        ).fail(
            function () {
                console.log("[brackets-nodejs] Failed to connect to nodejs. The server may be up because of another instance");
            }
        );
    });

    /**
     * The ConnectionManager helps to build and run request to execute a file on the serverside
     */
    var ConnectionManager = {

        last: {
            command: null,
            cwd: null
        },

        /**
         * Creates a new EventSource
         *
         * @param (optional): Command name
         * @param (optional): Execute in the current working directory
         * @param (optional): Directory to use as cwd
         */
        // This need to be inside quotes since new is a reserved word
        "new": function (command, useCurrentCwd, cwd) {

            if (source && source.close) source.close();

            // Current document
            var doc = DocumentManager.getCurrentDocument();
            if(!doc.file.isFile) return;

            // Build url
            var url = "http://" + config.host + ":" + config.port + "/?command=" + encodeURIComponent(command);
            var dir = null;
            if(useCurrentCwd) {
                dir = doc.file.parentPath;
            } else if(cwd) {
                dir = cwd;
            }

            if(dir !== null) {
                url += "&cwd=" + encodeURIComponent(dir);
            }

            // Add api version
            url += "&apiversion=" + API_VERSION;

            // Store the last command and cwd
            this.last.command = command;
            this.last.cwd = dir;

            // Server should be running
            source = new EventSource(url);

            source.addEventListener("message", function (msg) {
                Panel.write(msg.data);
            }, false);
            source.addEventListener("error", function () {
                source.close();
                Panel.write("Program exited.");
            }, false);

            Panel.show(command);
            Panel.clear();
        },

        newNpm: function (command) {

            var npmBin = get("npm");
            if(!npmBin) {
                npmBin = "npm";
            } else {
                // Add quotation because windows paths can contain spaces
                npmBin = '"' + npmBin + '"';
            }

            this.new(npmBin + " " + command, true);

        },

        newNode: function () {

            var nodeBin = get("node");
            if(!nodeBin) {
                nodeBin = "node";
            } else {
                // Add quotation because windows paths can contain spaces
                nodeBin = '"' + nodeBin + '"';
            }

            // Current document
            var doc = DocumentManager.getCurrentDocument();
            if(!doc.file.isFile) return;

            this.new(nodeBin + ' "' + doc.file.fullPath + '"', true);

        },

        rerun: function () {

            var last = this.last;
            if(last.command === null) return;

            this.new(last.command, false, last.cwd);

        },

        /**
         * Close the current connection if server is started
         */
        exit: function () {
            source.close();
        }
    };

    /**
     * Panel alias terminal
     */
    $(".content").append(require("text!html/panel.html"));
    var Panel = {

        id: "brackets-nodejs-terminal",
        panel: null,
        commandTitle: null,
        height: 201,

        get: function (qs) {
            return this.panel.querySelector(qs);
        },

        /**
         * Basic functionality
         */
        show: function (command) {
            this.panel.style.display = "block";
            this.commandTitle.textContent = command;
            EditorManager.resizeEditor();
        },
        hide: function () {
            this.panel.style.display = "none";
            EditorManager.resizeEditor();
        },
        clear: function () {
            this.pre.innerHTML = null;
        },

        /**
         * Prints a string into the terminal
         * It will be colored and then escape to prohibit XSS (Yes, inside an editor!)
         *
         * @param: String to be output
         */
        write: function (str) {
            var e = document.createElement("div");
            e.innerHTML = ansi(str.replace(/</g, "<").replace(/>/g, ">"));
            this.pre.appendChild(e);
        },

        /**
         * Used to enable resizing the panel
         */
        mousemove: function (e) {

            var h = Panel.height + (Panel.y - e.pageY);
            Panel.panel.style.height = h + "px";
            EditorManager.resizeEditor();

        },
        mouseup: function (e) {

            document.removeEventListener("mousemove", Panel.mousemove);
            document.removeEventListener("mouseup", Panel.mouseup);

            Panel.height = Panel.height + (Panel.y - e.pageY);

        },
        y: 0
    };

    // Still resizing
    Panel.panel = document.getElementById(Panel.id);
    Panel.commandTitle = Panel.get(".cmd");
    Panel.pre = Panel.get(".table-container pre");
    Panel.get(".resize").addEventListener("mousedown", function (e) {

        Panel.y = e.pageY;

        document.addEventListener("mousemove", Panel.mousemove);
        document.addEventListener("mouseup", Panel.mouseup);

    });

    /**
     * Terminal buttons
     */
    document.querySelector("#" + Panel.id + " .action-close").addEventListener("click", function () {
        ConnectionManager.exit();
        Panel.hide();
    });
    document.querySelector("#" + Panel.id + " .action-terminate").addEventListener("click", function () {
        ConnectionManager.exit();
    });
    document.querySelector("#" + Panel.id + " .action-rerun").addEventListener("click", function () {
        ConnectionManager.rerun();
    });

    var Dialog = {
        /**
         * The settings modal is used to configure the path to node's and node's binary
         * HTML : html/modal-settings.html
         */
        settings: {

            /**
             * HTML put inside the dialog
             */
            html: require("text!html/modal-settings.html"),

            /**
             * Opens up the modal
             */
            show: function () {
                Dialogs.showModalDialog(
                    NODE_SETTINGS_DIALOG_ID, // ID the specify the dialog
                    "Node.js-Configuration", // Title
                    this.html, // HTML-Content
                    [ // Buttons
                        {
                            className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
                            id: Dialogs.DIALOG_BTN_OK,
                            text: "Save"
                        }, {
                            className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
                            id: Dialogs.DIALOG_BTN_CANCEL,
                            text: "Cancel"
                        }
                    ]
                ).done(function (id) {

                    // Only saving
                    if (id !== "ok") return;

                    var node = nodeInput.value,
                        npm = npmInput.value;
                    console.log(node, npm);
                    if (node && node !== "") set("node", node);
                    else rm("node");

                    if (npm && npm !== "") set("npm", npm)
                    else rm("npm");

                });

                // It's important to get the elements after the modal is rendered but before the done event
                var nodeInput = document.querySelector("." + NODE_SETTINGS_DIALOG_ID + " .node"),
                    npmInput = document.querySelector("." + NODE_SETTINGS_DIALOG_ID + " .npm");
                nodeInput.value = get("node");
                npmInput.value = get("npm");

            }
        },

        /**
         * The install modal is used to install a module inside the directory of the current file
         * HTML: html/modal-install.html
         */
        install: {

            /**
             * HTML put inside the dialog
             */
            html: require("text!html/modal-install.html"),

            /**
             * Opens up the modal
             */
            show: function () {

                Dialogs.showModalDialog(
                    NODE_INSTALL_DIALOG_ID,
                    "Install module",
                    this.html, [{
                        className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
                        id: Dialogs.DIALOG_BTN_OK,
                        text: "Install"
                    }, {
                        className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
                        id: Dialogs.DIALOG_BTN_CANCEL,
                        text: "Cancel"
                    }]
                ).done(function (id) {

                    // Only saving
                    if (id !== "ok") return;

                    // Module name musn't be empty
                    if (name.value == "") {
                        Dialogs.showModalDialog(Dialogs.DIALOG_ID_ERROR, "Error", "Please enter a module name");
                        return;
                    }

                    // Should it be saved to package.json
                    var s = save.checked ? "--save" : "";

                    ConnectionManager.newNpm("install " + name.value + " " + s);

                });

                // It's important to get the elements after the modal is rendered but before the done event
                var name = document.querySelector("." + NODE_INSTALL_DIALOG_ID + " .name"),
                    save = document.querySelector("." + NODE_INSTALL_DIALOG_ID + " .save");

                name.focus();

            }
        },

        /**
         * The exec modal is used to execute a command
         * HTML: html/modal-install.html
         */
        exec: {

            /**
             * HTML put inside the dialog
             */
            html: require("text!html/modal-exec.html"),

            /**
             * Opens up the modal
             */
            show: function () {

                Dialogs.showModalDialog(
                    NODE_EXEC_DIALOG_ID,
                    "Execute command",
                    this.html, [{
                        className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
                        id: Dialogs.DIALOG_BTN_OK,
                        text: "Run"
                    }, {
                        className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
                        id: Dialogs.DIALOG_BTN_CANCEL,
                        text: "Cancel"
                    }]
                ).done(function (id) {

                    if (id !== "ok") return;

                    // Command musn't be empty
                    if (command.value == "") {
                        Dialogs.showModalDialog(Dialogs.DIALOG_ID_ERROR, "Error", "Please enter a command");
                        return;
                    }

                    // Should it be executed in the current working directory
                    var useCwd = !!cwd.checked;

                    ConnectionManager.new(command.value, useCwd);

                });

                // It's important to get the elements after the modal is rendered but before the done event
                var command = document.querySelector("." + NODE_EXEC_DIALOG_ID + " .command"),
                    cwd = document.querySelector("." + NODE_EXEC_DIALOG_ID + " .cwd");

                command.focus();

            }
        }
    };

    /**
     * Menu
     */
    var RUN_CMD_ID = "brackets-nodejs.run",
        EXEC_CMD_ID = "brackets-nodejs.exec",
        RUN_NPM_START_CMD_ID = "brackets-nodejs.run_npm_start",
        RUN_NPM_STOP_CMD_ID = "brackets-nodejs.run_npm_stop",
        RUN_NPM_TEST_CMD_ID = "brackets-nodejs.run_npm_test",
        RUN_NPM_INSTALL_CMD_ID = "brackets-nodejs.run_npm_install",
        INSTALL_CMD_ID = "brackets-nodejs.install",
        CONFIG_CMD_ID = "brackets-nodejs.config";
    CommandManager.register("Run", RUN_CMD_ID, function () {
        ConnectionManager.newNode();
    });
    CommandManager.register("Execute command", EXEC_CMD_ID, function() {
        Dialog.exec.show();
    });
    CommandManager.register("Run as npm start", RUN_NPM_START_CMD_ID, function () {
        ConnectionManager.newNpm("start");
    });
    CommandManager.register("Run as npm stop", RUN_NPM_STOP_CMD_ID, function () {
        ConnectionManager.newNpm("stop");
    });
    CommandManager.register("Run as npm test", RUN_NPM_TEST_CMD_ID, function () {
        ConnectionManager.newNpm("test");
    });
    CommandManager.register("Run as npm install", RUN_NPM_INSTALL_CMD_ID, function () {
        ConnectionManager.newNpm("install");
    });
    CommandManager.register("Install module...", INSTALL_CMD_ID, function () {
        Dialog.install.show();
    });
    CommandManager.register("Configuration...", CONFIG_CMD_ID, function () {
        Dialog.settings.show();

    });

    NodeMenu.addMenuItem(RUN_CMD_ID, "Alt-N");
    NodeMenu.addMenuItem(EXEC_CMD_ID);
    NodeMenu.addMenuDivider();
    NodeMenu.addMenuItem(RUN_NPM_START_CMD_ID);
    NodeMenu.addMenuItem(RUN_NPM_STOP_CMD_ID);
    NodeMenu.addMenuItem(RUN_NPM_TEST_CMD_ID);
    NodeMenu.addMenuItem(RUN_NPM_INSTALL_CMD_ID);
    NodeMenu.addMenuDivider();
    NodeMenu.addMenuItem(INSTALL_CMD_ID);
    NodeMenu.addMenuDivider();
    NodeMenu.addMenuItem(CONFIG_CMD_ID);

});
<commit_msg>scroll content with logs<commit_after>define(function (require, exports, module) {
    "use strict";

    /** --- MODULES --- **/
    var CommandManager = brackets.getModule("command/CommandManager"),
        Menus = brackets.getModule("command/Menus"),
        DocumentManager = brackets.getModule("document/DocumentManager"),
        EditorManager = brackets.getModule("editor/EditorManager"),
        ExtensionUtils = brackets.getModule("utils/ExtensionUtils"),
        NodeConnection = brackets.getModule("utils/NodeConnection"),
        Dialogs = brackets.getModule("widgets/Dialogs"),
        ansi = require("ansi"),
        nodeConnection = new NodeConnection(),
        NodeMenuID = "node-menu",
        NodeMenu = Menus.addMenu("Node.js", NodeMenuID),
        source = null,
        NODE_SETTINGS_DIALOG_ID = "node-settings-dialog",
        NODE_INSTALL_DIALOG_ID = "node-install-dialog",
        NODE_EXEC_DIALOG_ID = "node-exec-dialog",
        LS_PREFIX = "node-",
        API_VERSION = 1;

    /**
     * Shortcuts for localstorage with prefix
     */
    function get(name) {
        return localStorage.getItem(LS_PREFIX + name);
    }

    function set(name, value) {
        return localStorage.setItem(LS_PREFIX + name, value);
    }

    function rm(name) {
        return localStorage.removeItem(LS_PREFIX + name);
    }

    /**
     * Load the configuration
     */
    var config = JSON.parse(require("text!config.json"));

    /**
     * Start the node server
     */
    nodeConnection.connect(true).then(function () {
        nodeConnection.loadDomains(
            [ExtensionUtils.getModulePath(module, "server.js")],
            true
        ).then(
            function () {
                console.log("[brackets-nodejs] Connected to nodejs");
            }
        ).fail(
            function () {
                console.log("[brackets-nodejs] Failed to connect to nodejs. The server may be up because of another instance");
            }
        );
    });

    /**
     * The ConnectionManager helps to build and run request to execute a file on the serverside
     */
    var ConnectionManager = {

        last: {
            command: null,
            cwd: null
        },

        /**
         * Creates a new EventSource
         *
         * @param (optional): Command name
         * @param (optional): Execute in the current working directory
         * @param (optional): Directory to use as cwd
         */
        // This need to be inside quotes since new is a reserved word
        "new": function (command, useCurrentCwd, cwd) {

            if (source && source.close) source.close();

            // Current document
            var doc = DocumentManager.getCurrentDocument();
            if(!doc.file.isFile) return;

            // Build url
            var url = "http://" + config.host + ":" + config.port + "/?command=" + encodeURIComponent(command);
            var dir = null;
            if(useCurrentCwd) {
                dir = doc.file.parentPath;
            } else if(cwd) {
                dir = cwd;
            }

            if(dir !== null) {
                url += "&cwd=" + encodeURIComponent(dir);
            }

            // Add api version
            url += "&apiversion=" + API_VERSION;

            // Store the last command and cwd
            this.last.command = command;
            this.last.cwd = dir;

            // Server should be running
            source = new EventSource(url);

            source.addEventListener("message", function (msg) {
                Panel.write(msg.data);
            }, false);
            source.addEventListener("error", function () {
                source.close();
                Panel.write("Program exited.");
            }, false);

            Panel.show(command);
            Panel.clear();
        },

        newNpm: function (command) {

            var npmBin = get("npm");
            if(!npmBin) {
                npmBin = "npm";
            } else {
                // Add quotation because windows paths can contain spaces
                npmBin = '"' + npmBin + '"';
            }

            this.new(npmBin + " " + command, true);

        },

        newNode: function () {

            var nodeBin = get("node");
            if(!nodeBin) {
                nodeBin = "node";
            } else {
                // Add quotation because windows paths can contain spaces
                nodeBin = '"' + nodeBin + '"';
            }

            // Current document
            var doc = DocumentManager.getCurrentDocument();
            if(!doc.file.isFile) return;

            this.new(nodeBin + ' "' + doc.file.fullPath + '"', true);

        },

        rerun: function () {

            var last = this.last;
            if(last.command === null) return;

            this.new(last.command, false, last.cwd);

        },

        /**
         * Close the current connection if server is started
         */
        exit: function () {
            source.close();
        }
    };

    /**
     * Panel alias terminal
     */
    $(".content").append(require("text!html/panel.html"));
    var Panel = {

        id: "brackets-nodejs-terminal",
        panel: null,
        commandTitle: null,
        height: 201,

        get: function (qs) {
            return this.panel.querySelector(qs);
        },

        /**
         * Basic functionality
         */
        show: function (command) {
            this.panel.style.display = "block";
            this.commandTitle.textContent = command;
            EditorManager.resizeEditor();
        },
        hide: function () {
            this.panel.style.display = "none";
            EditorManager.resizeEditor();
        },
        clear: function () {
            this.pre.innerHTML = null;
        },

        /**
         * Prints a string into the terminal
         * It will be colored and then escape to prohibit XSS (Yes, inside an editor!)
         *
         * @param: String to be output
         */
        write: function (str) {
            var e = document.createElement("div");
            e.innerHTML = ansi(str.replace(/</g, "<").replace(/>/g, ">"));

            var scroll = false;
            if (this.pre.parentNode.scrollTop === 0 || this.pre.parentNode.scrollTop === this.pre.parentNode.scrollHeight || this.pre.parentNode.scrollHeight - this.pre.parentNode.scrollTop === this.pre.parentNode.clientHeight) {
                scroll = true;
            }

            this.pre.appendChild(e);

            if (scroll) {
                this.pre.parentNode.scrollTop = this.pre.parentNode.scrollHeight;
            }
        },

        /**
         * Used to enable resizing the panel
         */
        mousemove: function (e) {

            var h = Panel.height + (Panel.y - e.pageY);
            Panel.panel.style.height = h + "px";
            EditorManager.resizeEditor();

        },
        mouseup: function (e) {

            document.removeEventListener("mousemove", Panel.mousemove);
            document.removeEventListener("mouseup", Panel.mouseup);

            Panel.height = Panel.height + (Panel.y - e.pageY);

        },
        y: 0
    };

    // Still resizing
    Panel.panel = document.getElementById(Panel.id);
    Panel.commandTitle = Panel.get(".cmd");
    Panel.pre = Panel.get(".table-container pre");
    Panel.get(".resize").addEventListener("mousedown", function (e) {

        Panel.y = e.pageY;

        document.addEventListener("mousemove", Panel.mousemove);
        document.addEventListener("mouseup", Panel.mouseup);

    });

    /**
     * Terminal buttons
     */
    document.querySelector("#" + Panel.id + " .action-close").addEventListener("click", function () {
        ConnectionManager.exit();
        Panel.hide();
    });
    document.querySelector("#" + Panel.id + " .action-terminate").addEventListener("click", function () {
        ConnectionManager.exit();
    });
    document.querySelector("#" + Panel.id + " .action-rerun").addEventListener("click", function () {
        ConnectionManager.rerun();
    });

    var Dialog = {
        /**
         * The settings modal is used to configure the path to node's and node's binary
         * HTML : html/modal-settings.html
         */
        settings: {

            /**
             * HTML put inside the dialog
             */
            html: require("text!html/modal-settings.html"),

            /**
             * Opens up the modal
             */
            show: function () {
                Dialogs.showModalDialog(
                    NODE_SETTINGS_DIALOG_ID, // ID the specify the dialog
                    "Node.js-Configuration", // Title
                    this.html, // HTML-Content
                    [ // Buttons
                        {
                            className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
                            id: Dialogs.DIALOG_BTN_OK,
                            text: "Save"
                        }, {
                            className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
                            id: Dialogs.DIALOG_BTN_CANCEL,
                            text: "Cancel"
                        }
                    ]
                ).done(function (id) {

                    // Only saving
                    if (id !== "ok") return;

                    var node = nodeInput.value,
                        npm = npmInput.value;
                    console.log(node, npm);
                    if (node && node !== "") set("node", node);
                    else rm("node");

                    if (npm && npm !== "") set("npm", npm)
                    else rm("npm");

                });

                // It's important to get the elements after the modal is rendered but before the done event
                var nodeInput = document.querySelector("." + NODE_SETTINGS_DIALOG_ID + " .node"),
                    npmInput = document.querySelector("." + NODE_SETTINGS_DIALOG_ID + " .npm");
                nodeInput.value = get("node");
                npmInput.value = get("npm");

            }
        },

        /**
         * The install modal is used to install a module inside the directory of the current file
         * HTML: html/modal-install.html
         */
        install: {

            /**
             * HTML put inside the dialog
             */
            html: require("text!html/modal-install.html"),

            /**
             * Opens up the modal
             */
            show: function () {

                Dialogs.showModalDialog(
                    NODE_INSTALL_DIALOG_ID,
                    "Install module",
                    this.html, [{
                        className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
                        id: Dialogs.DIALOG_BTN_OK,
                        text: "Install"
                    }, {
                        className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
                        id: Dialogs.DIALOG_BTN_CANCEL,
                        text: "Cancel"
                    }]
                ).done(function (id) {

                    // Only saving
                    if (id !== "ok") return;

                    // Module name musn't be empty
                    if (name.value == "") {
                        Dialogs.showModalDialog(Dialogs.DIALOG_ID_ERROR, "Error", "Please enter a module name");
                        return;
                    }

                    // Should it be saved to package.json
                    var s = save.checked ? "--save" : "";

                    ConnectionManager.newNpm("install " + name.value + " " + s);

                });

                // It's important to get the elements after the modal is rendered but before the done event
                var name = document.querySelector("." + NODE_INSTALL_DIALOG_ID + " .name"),
                    save = document.querySelector("." + NODE_INSTALL_DIALOG_ID + " .save");

                name.focus();

            }
        },

        /**
         * The exec modal is used to execute a command
         * HTML: html/modal-install.html
         */
        exec: {

            /**
             * HTML put inside the dialog
             */
            html: require("text!html/modal-exec.html"),

            /**
             * Opens up the modal
             */
            show: function () {

                Dialogs.showModalDialog(
                    NODE_EXEC_DIALOG_ID,
                    "Execute command",
                    this.html, [{
                        className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
                        id: Dialogs.DIALOG_BTN_OK,
                        text: "Run"
                    }, {
                        className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
                        id: Dialogs.DIALOG_BTN_CANCEL,
                        text: "Cancel"
                    }]
                ).done(function (id) {

                    if (id !== "ok") return;

                    // Command musn't be empty
                    if (command.value == "") {
                        Dialogs.showModalDialog(Dialogs.DIALOG_ID_ERROR, "Error", "Please enter a command");
                        return;
                    }

                    // Should it be executed in the current working directory
                    var useCwd = !!cwd.checked;

                    ConnectionManager.new(command.value, useCwd);

                });

                // It's important to get the elements after the modal is rendered but before the done event
                var command = document.querySelector("." + NODE_EXEC_DIALOG_ID + " .command"),
                    cwd = document.querySelector("." + NODE_EXEC_DIALOG_ID + " .cwd");

                command.focus();

            }
        }
    };

    /**
     * Menu
     */
    var RUN_CMD_ID = "brackets-nodejs.run",
        EXEC_CMD_ID = "brackets-nodejs.exec",
        RUN_NPM_START_CMD_ID = "brackets-nodejs.run_npm_start",
        RUN_NPM_STOP_CMD_ID = "brackets-nodejs.run_npm_stop",
        RUN_NPM_TEST_CMD_ID = "brackets-nodejs.run_npm_test",
        RUN_NPM_INSTALL_CMD_ID = "brackets-nodejs.run_npm_install",
        INSTALL_CMD_ID = "brackets-nodejs.install",
        CONFIG_CMD_ID = "brackets-nodejs.config";
    CommandManager.register("Run", RUN_CMD_ID, function () {
        ConnectionManager.newNode();
    });
    CommandManager.register("Execute command", EXEC_CMD_ID, function() {
        Dialog.exec.show();
    });
    CommandManager.register("Run as npm start", RUN_NPM_START_CMD_ID, function () {
        ConnectionManager.newNpm("start");
    });
    CommandManager.register("Run as npm stop", RUN_NPM_STOP_CMD_ID, function () {
        ConnectionManager.newNpm("stop");
    });
    CommandManager.register("Run as npm test", RUN_NPM_TEST_CMD_ID, function () {
        ConnectionManager.newNpm("test");
    });
    CommandManager.register("Run as npm install", RUN_NPM_INSTALL_CMD_ID, function () {
        ConnectionManager.newNpm("install");
    });
    CommandManager.register("Install module...", INSTALL_CMD_ID, function () {
        Dialog.install.show();
    });
    CommandManager.register("Configuration...", CONFIG_CMD_ID, function () {
        Dialog.settings.show();

    });

    NodeMenu.addMenuItem(RUN_CMD_ID, "Alt-N");
    NodeMenu.addMenuItem(EXEC_CMD_ID);
    NodeMenu.addMenuDivider();
    NodeMenu.addMenuItem(RUN_NPM_START_CMD_ID);
    NodeMenu.addMenuItem(RUN_NPM_STOP_CMD_ID);
    NodeMenu.addMenuItem(RUN_NPM_TEST_CMD_ID);
    NodeMenu.addMenuItem(RUN_NPM_INSTALL_CMD_ID);
    NodeMenu.addMenuDivider();
    NodeMenu.addMenuItem(INSTALL_CMD_ID);
    NodeMenu.addMenuDivider();
    NodeMenu.addMenuItem(CONFIG_CMD_ID);

});
<|endoftext|>


<commit_before>var Cell = require('./Cell');
var Food = require('./Food');
var Virus = require('./Virus');

function MotherCell() {
    Cell.apply(this, Array.prototype.slice.call(arguments));

    this.cellType = 2;
    this.isSpiked = true;
    this.isMotherCell = true;       // Not to confuse bots
    this.setColor({ r: 0xce, g: 0x63, b: 0x63 });
    this.motherCellMinSize = 149;   // vanilla 149 (mass = 149*149/100 = 222.01)
    this.motherCellSpawnAmount = 2;
    if (!this._size) {
        this.setSize(this.motherCellMinSize);
    }
}

module.exports = MotherCell;
MotherCell.prototype = new Cell();

// Main Functions
MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function

MotherCell.prototype.canEat = function (cell) {
    var maxMass = this.gameServer.config.motherCellMaxMass;
    if (maxMass && this._mass >= maxMass) return false;
    return cell.cellType == 0 ||  // can eat player cell
           cell.cellType == 2 ||  // can eat virus
           cell.cellType == 3;    // can eat ejected mass
};

MotherCell.prototype.onUpdate = function () {
    if (this._size <= this.motherCellMinSize) {
        return;
    }
    var maxFood = this.gameServer.config.foodMaxAmount;
    if (this.gameServer.nodesFood.length >= maxFood) {
        return;
    }
    var size1 = this._size;
    var size2 = this.gameServer.config.foodMinSize;
    for (var i = 0; i < this.motherCellSpawnAmount; i++) {
        size1 = Math.sqrt(size1 * size1 - size2 * size2);
        size1 = Math.max(size1, this.motherCellMinSize);
        this.setSize(size1);

        // Spawn food with size2
        var angle = Math.random() * 2 * Math.PI;
        var r = this._size;
        var pos = {
            x: this.position.x + r * Math.sin(angle),
            y: this.position.y + r * Math.cos(angle)
        };

        // Spawn food
        var food = new Food(this.gameServer, null, pos, size2);
        food.setColor(this.gameServer.getRandomColor());
        this.gameServer.addNode(food);

        // Eject to random distance
        food.setBoost(32 + 32 * Math.random(), angle);

        if (this.gameServer.nodesFood.length >= maxFood || size1 <= this.motherCellMinSize) {
            break;
        }
    }
    this.gameServer.updateNodeQuad(this);
};

MotherCell.prototype.onAdd = function () {
};

MotherCell.prototype.onRemove = function () {
};
<commit_msg>Update MotherCell.js<commit_after>var Cell = require('./Cell');
var Food = require('./Food');
var Virus = require('./Virus');

function MotherCell() {
    Cell.apply(this, Array.prototype.slice.call(arguments));

    this.cellType = 2;
    this.isSpiked = true;
    this.isMotherCell = true;       // Not to confuse bots
    this.setColor({ r: 0xce, g: 0x63, b: 0x63 });
    this.motherCellMinSize = 149;   // vanilla 149 (mass = 149*149/100 = 222.01)
    this.motherCellSpawnAmount = 2;
    if (!this._size) {
        this.setSize(this.motherCellMinSize);
    }
}

module.exports = MotherCell;
MotherCell.prototype = new Cell();

// Main Functions
MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function

MotherCell.prototype.canEat = function (cell) {
    var maxMass = this.gameServer.config.motherCellMaxMass;
    if (maxMass && this._mass >= maxMass) return false;
    return cell.cellType == 0 ||  // can eat player cell
           cell.cellType == 2 ||  // can eat virus
           cell.cellType == 3;    // can eat ejected mass
};

MotherCell.prototype.onUpdate = function () {
    var maxFood = this.gameServer.config.foodMaxAmount;
    if (this.gameServer.nodesFood.length >= maxFood) {
        return;
    }
    var size1 = this._size;
    var size2 = this.gameServer.config.foodMinSize;
    for (var i = 0; i < this.motherCellSpawnAmount; i++) {
        size1 = Math.sqrt(size1 * size1 - size2 * size2);
        size1 = Math.max(size1, this.motherCellMinSize);
        this.setSize(size1);

        // Spawn food with size2
        var angle = Math.random() * 2 * Math.PI;
        var r = this._size;
        var pos = {
            x: this.position.x + r * Math.sin(angle),
            y: this.position.y + r * Math.cos(angle)
        };

        // Spawn food
        var food = new Food(this.gameServer, null, pos, size2);
        food.setColor(this.gameServer.getRandomColor());
        this.gameServer.addNode(food);

        // Eject to random distance
        food.setBoost(32 + 32 * Math.random(), angle);

        if (this.gameServer.nodesFood.length >= maxFood || size1 <= this.motherCellMinSize) {
            break;
        }
    }
    this.gameServer.updateNodeQuad(this);
};

MotherCell.prototype.onAdd = function () {
};

MotherCell.prototype.onRemove = function () {
};
<|endoftext|>


<commit_before>import {useContext, useEffect, useState} from 'react';
import AppContext from '../../AppContext';
import {ReactComponent as ThumbDownIcon} from '../../images/icons/thumbs-down.svg';
import {ReactComponent as ThumbUpIcon} from '../../images/icons/thumbs-up.svg';
import {ReactComponent as WarningIcon} from '../../images/icons/warning-fill.svg';
import setupGhostApi from '../../utils/api';
import {HumanReadableError} from '../../utils/errors';
import ActionButton from '../common/ActionButton';
import CloseButton from '../common/CloseButton';
import LoadingPage from './LoadingPage';

const React = require('react');

export const FeedbackPageStyles = `
    .gh-portal-feedback {

    }

    .gh-portal-feedback .gh-feedback-icon {
        padding: 10px 0;
        text-align: center;
        color: var(--brandcolor);
        width: 48px;
        margin: 0 auto;
    }

    .gh-portal-feedback .gh-feedback-icon.gh-feedback-icon-error {
        color: #f50b23;
    }

    .gh-portal-feedback .gh-portal-text-center {
        padding: 15px 0;
    }

    .gh-portal-confirm-title {
        line-height: inherit;
        text-align: center;
        box-sizing: border-box;
        margin: 0;
        margin-bottom: .4rem;
        font-size: 24px;
        font-weight: 700;
        letter-spacing: -.018em;
    }

    .gh-portal-confirm-button {
        width: 100%;
        margin-top: 3.6rem;
    }

    .gh-feedback-buttons-group {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 16px;
        margin-top: 3.6rem;
    }

    .gh-feedback-button {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        font-size: 1.4rem;
        line-height: 1.2;
        font-weight: 700;
        border: none;
        border-radius: 22px;
        padding: 12px 8px;
        color: #505050;
        background: none;
        cursor: pointer;
    }

    .gh-feedback-button::before {
        content: '';
        position: absolute;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        border-radius: inherit;
        background: currentColor;
        opacity: 0.10;
    }

    .gh-feedback-button-selected {
        box-shadow: inset 0 0 0 2px currentColor;
    }

    .gh-feedback-button svg {
        width: 24px;
        height: 24px;
        color: inherit;
    }

    .gh-feedback-button svg path {
        stroke-width: 4px;
    }
`;

function ErrorPage({error}) {
    const {onAction} = useContext(AppContext);

    return (

            <CloseButton />

                <WarningIcon />

It's not you, it's us

{error}

            <ActionButton
                style={{width: '100%'}}
                retry={false}
                onClick = {() => onAction('closePopup')}
                disabled={false}
                brandColor='#000000'
                label={'Close'}
                isRunning={false}
                tabindex='3'
                classes={'sticky bottom'}
            />

    );
}

const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
    const {onAction, brandColor} = useContext(AppContext);
    const [score, setScore] = useState(initialScore);

    const stopPropagation = (event) => {
        event.stopPropagation();
    };

    const close = () => {
        onAction('closePopup');
    };

    const submit = async (event) => {
        event.stopPropagation();
        await onConfirm(score);
    };

    const getButtonClassNames = (value) => {
        const baseClassName = 'gh-feedback-button';
        return value === score ? `${baseClassName} gh-feedback-button-selected` : baseClassName;
    };

    const getInlineStyles = (value) => {
        return value === score ? {color: brandColor} : {};
    };

    return (

Give feedback on this post

                <button
                    className={getButtonClassNames(1)}
                    style={getInlineStyles(1)}
                    onClick={() => setScore(1)}
                >
                    <ThumbUpIcon />
                    More like this
                </button>

                <button
                    className={getButtonClassNames(0)}
                    style={getInlineStyles(0)}
                    onClick={() => setScore(0)}
                >
                    <ThumbDownIcon />
                    Less like this
                </button>

            <ActionButton
                classes="gh-portal-confirm-button"
                retry={false}
                onClick={submit}
                disabled={false}
                brandColor={brandColor}
                label="Submit feedback"
                isRunning={loading}
                tabindex="3"
            />
            <CloseButton close={() => close(false)} />

    );
};

async function sendFeedback({siteUrl, uuid, postId, score}) {
    const ghostApi = setupGhostApi({siteUrl});
    await ghostApi.feedback.add({uuid, postId, score});
}

const LoadingFeedbackView = ({action}) => {
    useEffect(() => {
        action();
    });

    return <LoadingPage/>;
};

const ConfirmFeedback = ({positive}) => {
    const {onAction, brandColor} = useContext(AppContext);

    const icon = positive ? <ThumbUpIcon /> : <ThumbDownIcon />;

    return (

            <CloseButton />

                {icon}

Thanks for the feedback!

Your input helps shape what gets published.

            <ActionButton
                style={{width: '100%'}}
                retry={false}
                onClick = {() => onAction('closePopup')}
                disabled={false}
                brandColor={brandColor}
                label={'Close'}
                isRunning={false}
                tabindex='3'
                classes={'sticky bottom'}
            />

    );
};

export default function FeedbackPage() {
    const {site, pageData, member} = useContext(AppContext);
    const {uuid, postId, score: initialScore} = pageData;
    const [score, setScore] = useState(initialScore);
    const positive = score === 1;

    const isLoggedIn = !!member;

    const [confirmed, setConfirmed] = useState(isLoggedIn);
    const [loading, setLoading] = useState(isLoggedIn);
    const [error, setError] = useState(null);

    const doSendFeedback = async (selectedScore) => {
        setLoading(true);
        try {
            await sendFeedback({siteUrl: site.url, uuid, postId, score: selectedScore});
            setScore(selectedScore);
        } catch (e) {
            const text = HumanReadableError.getMessageFromError(e, 'There was a problem submitting your feedback. Please try again or contact the site owner.');
            setError(text);
        }
        setLoading(false);
    };

    const onConfirm = async (selectedScore) => {
        await doSendFeedback(selectedScore);
        setConfirmed(true);
    };

    // Case: failed
    if (error) {
        return <ErrorPage error={error} />;
    }

    if (!confirmed) {
        return (<ConfirmDialog onConfirm={onConfirm} loading={loading} initialScore={score} />);
    } else {
        if (loading) {
            return <LoadingFeedbackView action={doSendFeedback} />;
        }
    }

    return (<ConfirmFeedback positive={positive} />);
}
<commit_msg>Fix unauth error<commit_after>import {useContext, useEffect, useState} from 'react';
import AppContext from '../../AppContext';
import {ReactComponent as ThumbDownIcon} from '../../images/icons/thumbs-down.svg';
import {ReactComponent as ThumbUpIcon} from '../../images/icons/thumbs-up.svg';
import {ReactComponent as WarningIcon} from '../../images/icons/warning-fill.svg';
import setupGhostApi from '../../utils/api';
import {HumanReadableError} from '../../utils/errors';
import ActionButton from '../common/ActionButton';
import CloseButton from '../common/CloseButton';
import LoadingPage from './LoadingPage';

const React = require('react');

export const FeedbackPageStyles = `
    .gh-portal-feedback {

    }

    .gh-portal-feedback .gh-feedback-icon {
        padding: 10px 0;
        text-align: center;
        color: var(--brandcolor);
        width: 48px;
        margin: 0 auto;
    }

    .gh-portal-feedback .gh-feedback-icon.gh-feedback-icon-error {
        color: #f50b23;
    }

    .gh-portal-feedback .gh-portal-text-center {
        padding: 15px 0;
    }

    .gh-portal-confirm-title {
        line-height: inherit;
        text-align: center;
        box-sizing: border-box;
        margin: 0;
        margin-bottom: .4rem;
        font-size: 24px;
        font-weight: 700;
        letter-spacing: -.018em;
    }

    .gh-portal-confirm-button {
        width: 100%;
        margin-top: 3.6rem;
    }

    .gh-feedback-buttons-group {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 16px;
        margin-top: 3.6rem;
    }

    .gh-feedback-button {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        font-size: 1.4rem;
        line-height: 1.2;
        font-weight: 700;
        border: none;
        border-radius: 22px;
        padding: 12px 8px;
        color: #505050;
        background: none;
        cursor: pointer;
    }

    .gh-feedback-button::before {
        content: '';
        position: absolute;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        border-radius: inherit;
        background: currentColor;
        opacity: 0.10;
    }

    .gh-feedback-button-selected {
        box-shadow: inset 0 0 0 2px currentColor;
    }

    .gh-feedback-button svg {
        width: 24px;
        height: 24px;
        color: inherit;
    }

    .gh-feedback-button svg path {
        stroke-width: 4px;
    }
`;

function ErrorPage({error}) {
    const {onAction} = useContext(AppContext);

    return (

            <CloseButton />

                <WarningIcon />

It's not you, it's us

{error}

            <ActionButton
                style={{width: '100%'}}
                retry={false}
                onClick = {() => onAction('closePopup')}
                disabled={false}
                brandColor='#000000'
                label={'Close'}
                isRunning={false}
                tabindex='3'
                classes={'sticky bottom'}
            />

    );
}

const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
    const {onAction, brandColor} = useContext(AppContext);
    const [score, setScore] = useState(initialScore);

    const stopPropagation = (event) => {
        event.stopPropagation();
    };

    const close = () => {
        onAction('closePopup');
    };

    const submit = async (event) => {
        event.stopPropagation();
        await onConfirm(score);
    };

    const getButtonClassNames = (value) => {
        const baseClassName = 'gh-feedback-button';
        return value === score ? `${baseClassName} gh-feedback-button-selected` : baseClassName;
    };

    const getInlineStyles = (value) => {
        return value === score ? {color: brandColor} : {};
    };

    return (

Give feedback on this post

                <button
                    className={getButtonClassNames(1)}
                    style={getInlineStyles(1)}
                    onClick={() => setScore(1)}
                >
                    <ThumbUpIcon />
                    More like this
                </button>

                <button
                    className={getButtonClassNames(0)}
                    style={getInlineStyles(0)}
                    onClick={() => setScore(0)}
                >
                    <ThumbDownIcon />
                    Less like this
                </button>

            <ActionButton
                classes="gh-portal-confirm-button"
                retry={false}
                onClick={submit}
                disabled={false}
                brandColor={brandColor}
                label="Submit feedback"
                isRunning={loading}
                tabindex="3"
            />
            <CloseButton close={() => close(false)} />

    );
};

async function sendFeedback({siteUrl, uuid, postId, score}) {
    const ghostApi = setupGhostApi({siteUrl});
    await ghostApi.feedback.add({uuid, postId, score});
}

const LoadingFeedbackView = ({action, score}) => {
    useEffect(() => {
        action(score);
    });

    return <LoadingPage/>;
};

const ConfirmFeedback = ({positive}) => {
    const {onAction, brandColor} = useContext(AppContext);

    const icon = positive ? <ThumbUpIcon /> : <ThumbDownIcon />;

    return (

            <CloseButton />

                {icon}

Thanks for the feedback!

Your input helps shape what gets published.

            <ActionButton
                style={{width: '100%'}}
                retry={false}
                onClick = {() => onAction('closePopup')}
                disabled={false}
                brandColor={brandColor}
                label={'Close'}
                isRunning={false}
                tabindex='3'
                classes={'sticky bottom'}
            />

    );
};

export default function FeedbackPage() {
    const {site, pageData, member} = useContext(AppContext);
    const {uuid, postId, score: initialScore} = pageData;
    const [score, setScore] = useState(initialScore);
    const positive = score === 1;

    const isLoggedIn = !!member;

    const [confirmed, setConfirmed] = useState(isLoggedIn);
    const [loading, setLoading] = useState(isLoggedIn);
    const [error, setError] = useState(null);

    const doSendFeedback = async (selectedScore) => {
        setLoading(true);
        try {
            await sendFeedback({siteUrl: site.url, uuid, postId, score: selectedScore});
            setScore(selectedScore);
        } catch (e) {
            const text = HumanReadableError.getMessageFromError(e, 'There was a problem submitting your feedback. Please try again or contact the site owner.');
            setError(text);
        }
        setLoading(false);
    };

    const onConfirm = async (selectedScore) => {
        await doSendFeedback(selectedScore);
        setConfirmed(true);
    };

    // Case: failed
    if (error) {
        return <ErrorPage error={error} />;
    }

    if (!confirmed) {
        return (<ConfirmDialog onConfirm={onConfirm} loading={loading} initialScore={score} />);
    } else {
        if (loading) {
            return <LoadingFeedbackView action={doSendFeedback} score={score} />;
        }
    }

    return (<ConfirmFeedback positive={positive} />);
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import 'select2';

export default class Select2 extends Component {
  static propTypes = {
    defaultValue: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.array,
    ]),
    data: PropTypes.array,
    events: PropTypes.array,
    options: PropTypes.object,
    multiple: PropTypes.bool,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
    onUnselect: PropTypes.func,
  }

  static defaultProps = {
    data: [],
    events: [
      ['change', 'onChange'],
      ['select2:open', 'onOpen'],
      ['select2:close', 'onClose'],
      ['select2:select', 'onSelect'],
      ['select2:unselect', 'onUnselect'],
    ],
    options: {},
    multiple: false,
  }

  constructor(props) {
    super(props);
    this.el = null;
  }

  componentDidMount() {
    this.el = $(ReactDOM.findDOMNode(this));
    this.el.select2(this.props.options);

    this.props.events.forEach(event => {
      this.el.on(event[0], this.props[event[1]]);
    });
  }

  componentWillUnmount() {
    this.el.select2('destroy');
  }

  render() {
    return (
      <select {...props}>
        {this.props.data.map((item, k) => {
          if (typeof item === 'string' ||
            ((!!item && typeof item === 'object') && Object.prototype.toString.call(item) === '[object String]')) {
            return (<option key={'option-' + k} value={item}>{item}</option>);
          }
          return (<option key={'option-' + k} value={item.id}>{item.text}</option>);
        })}
      </select>
    );
  }
}
<commit_msg>Fix missing this and separate paramters from data<commit_after>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import 'select2';

export default class Select2 extends Component {
  static propTypes = {
    defaultValue: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.array,
    ]),
    data: PropTypes.array,
    events: PropTypes.array,
    options: PropTypes.object,
    multiple: PropTypes.bool,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
    onUnselect: PropTypes.func,
  }

  static defaultProps = {
    data: [],
    events: [
      ['change', 'onChange'],
      ['select2:open', 'onOpen'],
      ['select2:close', 'onClose'],
      ['select2:select', 'onSelect'],
      ['select2:unselect', 'onUnselect'],
    ],
    options: {},
    multiple: false,
  }

  constructor(props) {
    super(props);
    this.el = null;
  }

  componentDidMount() {
    this.el = $(ReactDOM.findDOMNode(this));
    this.el.select2(this.props.options);

    this.props.events.forEach(event => {
      this.el.on(event[0], this.props[event[1]]);
    });
  }

  componentWillUnmount() {
    this.el.select2('destroy');
  }

  render() {
    const { data, events, ...params } = this.props;

    return (
      <select {...params}>
        {this.props.data.map((item, k) => {
          if (typeof item === 'string' ||
            ((!!item && typeof item === 'object') && Object.prototype.toString.call(item) === '[object String]')) {
            return (<option key={'option-' + k} value={item}>{item}</option>);
          }
          return (<option key={'option-' + k} value={item.id}>{item.text}</option>);
        })}
      </select>
    );
  }
}
<|endoftext|>


<commit_before>function layout(el, targetLineLength, width, height) {
    if (width === undefined) {
        width = el.clientWidth;
    }
    if (height === undefined) {
        height = el.clientHeight;
    }

    // First, use Erik Loyer's slabtype algorithm to split our input text into
    // suitable lines. We depart from the original algorithm by requiring the
    // ideal line length to be given instead of automagically calculated.
    //
    // http://erikloyer.com/index.php/blog/the_slabtype_algorithm_part_1_background/
    var text = el.innerText;
    var words = text.split(/\s+/);

    var lines = [];
    var preText, postText, lineText;
    var preDiff, postDiff;
    var wordIndex = 0;

    while (wordIndex < words.length) {
        postText = '';
        while (postText.length < targetLineLength) {
            preText = postText;
            postText += words[wordIndex] + ' ';
            wordIndex++;
            if (wordIndex >= words.length) {
                break;
            }
        }

        preText = preText.trim();
        postText = postText.trim();
        preDiff = targetLineLength - preText.length;
        postDiff = postText.length - targetLineLength;

        if (preText && preDiff < postDiff) {
            lineText = preText;
            wordIndex--;
        } else {
            lineText = postText;
        }
        lines.push(lineText);
    }

    // Add our lines to the DOM, where each line is wrapped in a  and all
    // of the spans are wrapped in a
.
    var spans = [];
    var line;
    for (var i = 0; i < lines.length; i++) {
        line = lines[i];
        spans.push(
            '' +
            line +
            ''
        );
    }
    el.innerHTML = (
        '
' +
        spans.join('\n') +
        '
'
    );

    // Lay out the lines optimally within the given bounds.
    var lineEls = el.querySelectorAll('span');
    var lineCount = lineEls.length;
    var lineEl;
    var scale;
    var rect;
    var totalHeight = 0;
    for (i = 0; i < lineCount; i++) {
        lineEl = lineEls[i];
        scale = width / lineEl.offsetWidth;
        if (scale !== 1) {
            lineEl.style['-webkit-transform'] = 'scale(' + scale + ',' + scale + ')';
            lineEl.style['top'] = totalHeight + 'px';
            rect = lineEl.getBoundingClientRect();
            totalHeight += rect.height;
        }
    }

    var wrapper = el.querySelector('.slabtext');
    var offset;
    if (totalHeight <= height) {
        // Our text fits, so center it vertically
        offset = (height - totalHeight) / 2;
        wrapper.style['-webkit-transform'] = 'translateY(' + offset + 'px)';
    } else {
        // Our text is too tall, so scale the whole container down and center
        // it horizontally.
        scale = height / totalHeight;
        offset = (width - (width * scale)) / 2;
        wrapper.style['-webkit-transform'] = 'scale(' + scale + ',' + scale + ')';
        wrapper.style['left'] = offset + 'px';
    }

    return totalHeight;
}

module.exports = {
    'layout': layout
};
<commit_msg>centering is free (for overflows) w/ diff transform origin<commit_after>function layout(el, targetLineLength, width, height) {
    if (width === undefined) {
        width = el.clientWidth;
    }
    if (height === undefined) {
        height = el.clientHeight;
    }

    // First, use Erik Loyer's slabtype algorithm to split our input text into
    // suitable lines. We depart from the original algorithm by requiring the
    // ideal line length to be given instead of automagically calculated.
    //
    // http://erikloyer.com/index.php/blog/the_slabtype_algorithm_part_1_background/
    var text = el.innerText;
    var words = text.split(/\s+/);

    var lines = [];
    var preText, postText, lineText;
    var preDiff, postDiff;
    var wordIndex = 0;

    while (wordIndex < words.length) {
        postText = '';
        while (postText.length < targetLineLength) {
            preText = postText;
            postText += words[wordIndex] + ' ';
            wordIndex++;
            if (wordIndex >= words.length) {
                break;
            }
        }

        preText = preText.trim();
        postText = postText.trim();
        preDiff = targetLineLength - preText.length;
        postDiff = postText.length - targetLineLength;

        if (preText && preDiff < postDiff) {
            lineText = preText;
            wordIndex--;
        } else {
            lineText = postText;
        }
        lines.push(lineText);
    }

    // Add our lines to the DOM, where each line is wrapped in a  and all
    // of the spans are wrapped in a
.
    var spans = [];
    var line;
    for (var i = 0; i < lines.length; i++) {
        line = lines[i];
        spans.push(
            '' +
            line +
            ''
        );
    }
    el.innerHTML = (
        '
' +
        spans.join('\n') +
        '
'
    );

    // Lay out the lines optimally within the given bounds.
    var lineEls = el.querySelectorAll('span');
    var lineCount = lineEls.length;
    var lineEl;
    var scale;
    var rect;
    var totalHeight = 0;
    for (i = 0; i < lineCount; i++) {
        lineEl = lineEls[i];
        scale = width / lineEl.offsetWidth;
        if (scale !== 1) {
            lineEl.style['-webkit-transform'] = 'scale(' + scale + ',' + scale + ')';
            lineEl.style['top'] = totalHeight + 'px';
            rect = lineEl.getBoundingClientRect();
            totalHeight += rect.height;
        }
    }

    var wrapper = el.querySelector('.slabtext');
    if (totalHeight <= height) {
        // Our text fits, so center it vertically
        var offset = (height - totalHeight) / 2;
        wrapper.style['-webkit-transform'] = 'translateY(' + offset + 'px)';
    } else {
        // Our text is too tall, so scale the whole container down and center
        // it horizontally.
        scale = height / totalHeight;
        wrapper.style['-webkit-transform'] = 'scale(' + scale + ',' + scale + ')';
    }

    return totalHeight;
}

module.exports = {
    'layout': layout
};
<|endoftext|>