<commit_before>class NauticalFlagsTypeset {
  constructor(){
    this.font = undefined;

    this.styleStack = [];
    this.printBlocks = [];

    this.mode = NauticalFlagsTypeset.MODE_TEXT;
  }
  static get MODE_SINGLE(){ return 0; }
  static get MODE_TEXT(){ return 1; }

  setFont(newFont) {
    this.font = newFont;
    this.supportedKeybuffers = Object.getOwnPropertyNames(nauticalFlags.__proto__)
                                     .filter(item => typeof nauticalFlags[item] === 'function')
                                     .filter(name => name.startsWith('drawSpecial') )
                                     .map(name => name.replace('drawSpecial', ''));
  }

  get fontWidth(){
    return this.font.flagWidth;
  }

  getProperties(){
    return {
      fontWidth: this.fontWidth
    };
  }

  textSize(newSize){
    this.font.flagWidth = newSize;
  }

  push(){
    this.styleStack.push(this.getProperties());
  }

  pop(){
    if (this.styleStack.length == 0){
      return;
    }
    this.applyStyle( this.styleStack.pop() );
  }

  applyStyle(style){
    this.font.flagWidth = style.fontWidth;
  }

  text(str, x, y, w, h){
    x = x || 0;
    y = y || 0;
    w = w || width;
    h = h || height - y;
    let textAlignment = textAlign();

    const letterSpacing = this.fontWidth * 0.1;

    const textWidth = str.length * this.fontWidth
                        + (str.length - 1) * letterSpacing;
    let startX;
    if (textAlignment.horizontal == CENTER){
      startX = x + (w - textWidth) / 2;
    } else if (textAlignment.horizontal == RIGHT){
      startX = x + w - textWidth;
    } else {
      // ALIGN LEFT
      startX = x;
    }

    noStroke();
    push();
    translate(startX, y);
    const symbols = str.split('');

    for(var i = 0; i < symbols.length; i++){
      let renderMethodName = this.renderMethodFor(symbols[i]);
      this.font[renderMethodName]();
      translate(this.fontWidth + letterSpacing, 0);
    }
    pop();
  }

  _renderVia(methodName){
    if (undefined === this.font[methodName]){
      console.warn('Unsupported render method: ' + methodName);
      return false;
    }

    if (this.mode == NauticalFlagsTypeset.MODE_TEXT){
      this.printBlocks.push(methodName);
      return this._renderPrintBlocks();
    } else {
      return this._renderSingle(methodName);
    }
  }

  _renderSingle(methodName){
    background(50);
    noStroke();
    push();
    translate(width / 2 - this.fontWidth / 2, height / 2 - this.fontWidth / 2);

    this.font[methodName]();
    pop();
    return true;
  }

  _renderPrintBlocks(){
    this.pos = createVector(20, 20);
    noStroke();
    background(50);
    push();
    translate(this.pos.x, this.pos.y);

    let letterSpacing = 0.1 * this.fontWidth;
    let blockOffset = this.fontWidth + letterSpacing;
    let startX = this.pos.x;

    for(var i = 0; i < this.printBlocks.length; i++){
      let renderMethodName = this.printBlocks[i];
      this.font[renderMethodName]();

      if ((this.pos.x + blockOffset + this.fontWidth) > width){
        translate(startX - this.pos.x, blockOffset);
        this.pos.x = startX;
        this.pos.y += blockOffset;
      } else {
        this.pos.x += blockOffset;
        translate(blockOffset, 0);
        // translate(this.fontWidth + letterSpacing, 0);
      }
    }
    pop();
    return true;
  }

  renderKey(){
    return this._renderVia( this.renderMethodFor(key) );
  }

  renderBuffer(keybuffer){
    return this._renderVia( this.renderMethodForBuffer(keybuffer) );
  }

  pennantNumberFromKey(key){
    return ')!@#$%^&*('.indexOf(key);
  }

  renderMethodFor(key){
    const supportedFlags = /^[a-z0-9]$/i;
    if (supportedFlags.test(key)) {
      return 'draw' + key.toUpperCase();
    }

    let pennantNumber = this.pennantNumberFromKey(key);
    if (pennantNumber >= 0){
      return 'drawNato' + pennantNumber;
    }

    if (key == ' '){
      return 'drawSpace';
    }

    if (key == '.'){
      return 'drawCodeFlag';
    }
    return undefined;
  }

  renderMethodForBuffer(keybuffer){
    if (keybuffer.length == 1 && keybuffer[0] >= '1' && keybuffer[0] <= '4'){
      return 'drawSubstitute' + key;
    } else {
      let flagCode = keybuffer.join('').toUpperCase();
      if (this.supportedKeybuffers.includes(flagCode)){
        return 'drawSpecial' + flagCode;
      }
    }
  }
}
<commit_msg>p5js/nautical-flags-02 - Fix bug related to setting a new font-size<commit_after>class NauticalFlagsTypeset {
  constructor(){
    this.font = undefined;

    this.styleStack = [];
    this.printBlocks = [];

    this.mode = NauticalFlagsTypeset.MODE_TEXT;
  }
  static get MODE_SINGLE(){ return 0; }
  static get MODE_TEXT(){ return 1; }

  setFont(newFont) {
    this.font = newFont;
    this.supportedKeybuffers = Object.getOwnPropertyNames(nauticalFlags.__proto__)
                                     .filter(item => typeof nauticalFlags[item] === 'function')
                                     .filter(name => name.startsWith('drawSpecial') )
                                     .map(name => name.replace('drawSpecial', ''));
  }

  get fontWidth(){
    return this.font.flagWidth;
  }

  getProperties(){
    return {
      fontWidth: this.fontWidth
    };
  }

  textSize(newSize){
    this.font.nWidth = newSize;
  }

  push(){
    this.styleStack.push(this.getProperties());
  }

  pop(){
    if (this.styleStack.length == 0){
      return;
    }
    this.applyStyle( this.styleStack.pop() );
  }

  applyStyle(style){
    this.font.nWidth = style.fontWidth;
  }

  text(str, x, y, w, h){
    x = x || 0;
    y = y || 0;
    w = w || width;
    h = h || height - y;
    let textAlignment = textAlign();

    const letterSpacing = this.fontWidth * 0.1;

    const textWidth = str.length * this.fontWidth
                        + (str.length - 1) * letterSpacing;
    let startX;
    if (textAlignment.horizontal == CENTER){
      startX = x + (w - textWidth) / 2;
    } else if (textAlignment.horizontal == RIGHT){
      startX = x + w - textWidth;
    } else {
      // ALIGN LEFT
      startX = x;
    }

    noStroke();
    push();
    translate(startX, y);
    const symbols = str.split('');

    for(var i = 0; i < symbols.length; i++){
      let renderMethodName = this.renderMethodFor(symbols[i]);
      this.font[renderMethodName]();
      translate(this.fontWidth + letterSpacing, 0);
    }
    pop();
  }

  _renderVia(methodName){
    if (undefined === this.font[methodName]){
      console.warn('Unsupported render method: ' + methodName);
      return false;
    }

    if (this.mode == NauticalFlagsTypeset.MODE_TEXT){
      this.printBlocks.push(methodName);
      return this._renderPrintBlocks();
    } else {
      return this._renderSingle(methodName);
    }
  }

  _renderSingle(methodName){
    background(50);
    noStroke();
    push();
    translate(width / 2 - this.fontWidth / 2, height / 2 - this.fontWidth / 2);

    this.font[methodName]();
    pop();
    return true;
  }

  _renderPrintBlocks(){
    this.pos = createVector(20, 20);
    noStroke();
    background(50);
    push();
    translate(this.pos.x, this.pos.y);

    let letterSpacing = 0.1 * this.fontWidth;
    let blockOffset = this.fontWidth + letterSpacing;
    let startX = this.pos.x;

    for(var i = 0; i < this.printBlocks.length; i++){
      let renderMethodName = this.printBlocks[i];
      this.font[renderMethodName]();

      if ((this.pos.x + blockOffset + this.fontWidth) > width){
        translate(startX - this.pos.x, blockOffset);
        this.pos.x = startX;
        this.pos.y += blockOffset;
      } else {
        this.pos.x += blockOffset;
        translate(blockOffset, 0);
        // translate(this.fontWidth + letterSpacing, 0);
      }
    }
    pop();
    return true;
  }

  renderKey(){
    return this._renderVia( this.renderMethodFor(key) );
  }

  renderBuffer(keybuffer){
    return this._renderVia( this.renderMethodForBuffer(keybuffer) );
  }

  pennantNumberFromKey(key){
    return ')!@#$%^&*('.indexOf(key);
  }

  renderMethodFor(key){
    const supportedFlags = /^[a-z0-9]$/i;
    if (supportedFlags.test(key)) {
      return 'draw' + key.toUpperCase();
    }

    let pennantNumber = this.pennantNumberFromKey(key);
    if (pennantNumber >= 0){
      return 'drawNato' + pennantNumber;
    }

    if (key == ' '){
      return 'drawSpace';
    }

    if (key == '.'){
      return 'drawCodeFlag';
    }
    return undefined;
  }

  renderMethodForBuffer(keybuffer){
    if (keybuffer.length == 1 && keybuffer[0] >= '1' && keybuffer[0] <= '4'){
      return 'drawSubstitute' + key;
    } else {
      let flagCode = keybuffer.join('').toUpperCase();
      if (this.supportedKeybuffers.includes(flagCode)){
        return 'drawSpecial' + flagCode;
      }
    }
  }
}
<|endoftext|>


<commit_before>var util = require('../lib/test-utils.js');
var expect = require('chai').expect;

describe('fs.mkdir', function () {
  beforeEach(util.setup);
  afterEach(util.cleanup);

  it('should be a function', function () {
    var fs = util.fs();
    expect(fs.mkdir).to.be.a('function');
  });

  it('should return an error if part of the parent path does not exist', function (done) {
    var fs = util.fs();

    fs.mkdir('/tmp/mydir', function (error) {
      expect(error).to.exist;
      expect(error.code).to.equal('ENOENT');
      done();
    });
  });

  it('should return an error if the path already exists', function (done) {
    var fs = util.fs();

    fs.mkdir('/', function (error) {
      expect(error).to.exist;
      expect(error.code).to.equal('EEXIST');
      done();
    });
  });

  it('should make a new directory', function (done) {
    var fs = util.fs();

    fs.mkdir('/tmp', function (error) {
      expect(error).not.to.exist;
      if (error) throw error;

      fs.stat('/tmp', function (error, stats) {
        expect(error).not.to.exist;
        expect(stats).to.exist;
        expect(stats.isDirectory()).to.be.true;
        done();
      });
    });
  });
});

describe('fs.promises.mkdir', function () {
  beforeEach(util.setup);
  afterEach(util.cleanup);

  it('should be a function', function () {
    var fs = util.fs();
    expect(fs.promises.mkdir).to.be.a('function');
  });

  it('should return an error if part of the parent path does not exist', function () {
    var fs = util.fs();

    return fs.promises.mkdir('/tmp/mydir')
      .catch(error => {
        expect(error).to.exist;
        expect(error.code).to.equal('ENOENT');
      });
  });

  it('should return an error if the path already exists', function () {
    var fs = util.fs();

    return fs.promises.mkdir('/')
      .catch(error =>{
        expect(error).to.exist;
        expect(error.code).to.equal('EEXIST');
      });
  });

  it('should make a new directory', function () {
    var fs = util.fs();

    return fs.promises.mkdir('/tmp')
      .then(() => fs.promises.stat('/tmp'))
      .then(stats => {
        expect(stats).to.exist;
        expect(stats.isDirectory()).to.be.true;
      });
  });

  it('should return a promise', function () {
    var fs = util.fs();
    expect(fs.promises.mkdir('/tmp')).to.be.a('promise');
  });
});<commit_msg>Fix issue #672 : Refactor "filer/tests/spec/fs.mkdir.spec.js" to follow ES6 syntax with "const/let" instead of "var" (#722)<commit_after>'use strict';
const util = require('../lib/test-utils.js');
const expect = require('chai').expect;

describe('fs.mkdir', function () {
  beforeEach(util.setup);
  afterEach(util.cleanup);

  it('should be a function', function () {
    const fs = util.fs();
    expect(fs.mkdir).to.be.a('function');
  });

  it('should return an error if part of the parent path does not exist', function (done) {
    const fs = util.fs();

    fs.mkdir('/tmp/mydir', function (error) {
      expect(error).to.exist;
      expect(error.code).to.equal('ENOENT');
      done();
    });
  });

  it('should return an error if the path already exists', function (done) {
    const fs = util.fs();

    fs.mkdir('/', function (error) {
      expect(error).to.exist;
      expect(error.code).to.equal('EEXIST');
      done();
    });
  });

  it('should make a new directory', function (done) {
    const fs = util.fs();

    fs.mkdir('/tmp', function (error) {
      expect(error).not.to.exist;
      if (error) throw error;

      fs.stat('/tmp', function (error, stats) {
        expect(error).not.to.exist;
        expect(stats).to.exist;
        expect(stats.isDirectory()).to.be.true;
        done();
      });
    });
  });
});

describe('fs.promises.mkdir', function () {
  beforeEach(util.setup);
  afterEach(util.cleanup);

  it('should be a function', function () {
    const fs = util.fs();
    expect(fs.promises.mkdir).to.be.a('function');
  });

  it('should return an error if part of the parent path does not exist', function () {
    const fs = util.fs();

    return fs.promises.mkdir('/tmp/mydir')
      .catch(error => {
        expect(error).to.exist;
        expect(error.code).to.equal('ENOENT');
      });
  });

  it('should return an error if the path already exists', function () {
    const fs = util.fs();

    return fs.promises.mkdir('/')
      .catch(error =>{
        expect(error).to.exist;
        expect(error.code).to.equal('EEXIST');
      });
  });

  it('should make a new directory', function () {
    const fs = util.fs();

    return fs.promises.mkdir('/tmp')
      .then(() => fs.promises.stat('/tmp'))
      .then(stats => {
        expect(stats).to.exist;
        expect(stats.isDirectory()).to.be.true;
      });
  });

  it('should return a promise', function () {
    const fs = util.fs();
    expect(fs.promises.mkdir('/tmp')).to.be.a('promise');
  });
});<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import Ink from 'react-ink'
import styles from 'part:@sanity/components/buttons/default-style'
import Spinner from 'part:@sanity/components/loading/spinner'

export default class DefaultButton extends React.Component {
  static propTypes = {
    kind: PropTypes.oneOf(['default', 'simple', 'secondary']),
    color: PropTypes.oneOf(['primary', 'success', 'danger', 'white']),
    onClick: PropTypes.func,
    children: PropTypes.node,
    inverted: PropTypes.bool,
    icon: PropTypes.func,
    loading: PropTypes.bool,
    ripple: PropTypes.bool,
    className: PropTypes.string,
    disabled: PropTypes.bool,
    tabIndex: PropTypes.number
  }

  static defaultProps = {
    ripple: true,
    icon: null,
    onClick() {},
    kind: 'default'
  }

  constructor(...args) {
    super(...args)

    this.handleClick = this.handleClick.bind(this)

    this.state = {
      recentlyHovered: false
    }
  }

  handleClick(event) {
    this.setState({
      recentlyHovered: true
    })
    this.props.onClick(event)
    clearTimeout(this.timeOut)
    this.timeOut = setTimeout(() => {
      this.setState({
        recentlyHovered: true
      })
    }, 300)
  }

  componentWillUnmount() {
    clearTimeout(this.timeOut)
  }

  render() {

    const {kind, ripple, disabled, inverted, color, icon, loading, className, children, ...rest} = this.props

    const Icon = icon

    const style = `
      ${(inverted ? styles.inverted : '')}
      ${kind ? styles[kind] : styles.default}
      ${color ? styles[`color__${color}`] : ''}
      ${Icon ? styles.hasIcon : ''}
      ${className || ''}
      ${disabled ? styles.disabled : ''}
      ${this.state.recentlyHovered ? styles.recentlyHovered : styles.notRecentlyHovered}`

    return (
      <button
        {...rest}
        className={style}
        type="button"
        onClick={this.handleClick}
        disabled={disabled}
      >

          {
            loading && <Spinner />
          }
          {
            Icon && <Icon className={styles.icon} />
          }
          {
            children && {children}
          }
          {
            ripple && !disabled && <Ink duration={200} opacity={0.10} radius={200} />
          }

      </button>
    )
  }
}
<commit_msg>[components] Filter class names on default button to prevent 'undefined' being part of the output<commit_after>import React, {PropTypes} from 'react'
import Ink from 'react-ink'
import styles from 'part:@sanity/components/buttons/default-style'
import Spinner from 'part:@sanity/components/loading/spinner'

export default class DefaultButton extends React.Component {
  static propTypes = {
    kind: PropTypes.oneOf(['default', 'simple', 'secondary']),
    color: PropTypes.oneOf(['primary', 'success', 'danger', 'white']),
    onClick: PropTypes.func,
    children: PropTypes.node,
    inverted: PropTypes.bool,
    icon: PropTypes.func,
    loading: PropTypes.bool,
    ripple: PropTypes.bool,
    className: PropTypes.string,
    disabled: PropTypes.bool,
    tabIndex: PropTypes.number
  }

  static defaultProps = {
    ripple: true,
    icon: null,
    onClick() {},
    kind: 'default'
  }

  constructor(...args) {
    super(...args)

    this.handleClick = this.handleClick.bind(this)

    this.state = {
      recentlyHovered: false
    }
  }

  handleClick(event) {
    this.setState({
      recentlyHovered: true
    })
    this.props.onClick(event)
    clearTimeout(this.timeOut)
    this.timeOut = setTimeout(() => {
      this.setState({
        recentlyHovered: true
      })
    }, 300)
  }

  componentWillUnmount() {
    clearTimeout(this.timeOut)
  }

  render() {

    const {kind, ripple, disabled, inverted, color, icon, loading, className, children, ...rest} = this.props

    const Icon = icon

    const style = [
      inverted && styles.inverted,
      kind ? styles[kind] : styles.default,
      color && styles[`color__${color}`],
      icon && styles.hasIcon,
      className,
      disabled && styles.disabled,
      this.state.recentlyHovered ? styles.recentlyHovered : styles.notRecentlyHovered
    ].filter(Boolean).join(' ')

    return (
      <button
        {...rest}
        className={style}
        type="button"
        onClick={this.handleClick}
        disabled={disabled}
      >

          {
            loading && <Spinner />
          }
          {
            Icon && <Icon className={styles.icon} />
          }
          {
            children && {children}
          }
          {
            ripple && !disabled && <Ink duration={200} opacity={0.10} radius={200} />
          }

      </button>
    )
  }
}
<|endoftext|>


<commit_before>import config from 'config:@sanity/data-aspects'
import {startCase} from 'lodash'
const bundledTypeNames = ['geopoint', 'imageAsset', 'fileAsset']

class DataAspectsResolver {

  constructor(schema) {
    this.schema = schema
    this.config = Object.assign({typeOptions: {}}, config || {})
  }

  getConfig() {
    return this.config
  }

  getField(type, fieldName) {
    return type.fields.filter(currField => currField.name === fieldName)
  }

  getType(typeName) {
    return this.schema.get(typeName)
  }

  getInferredTypes() {
    let defaultTypes = this.schema.getTypeNames() || []
    defaultTypes = defaultTypes.filter(typeName => {
      // Exclude types which come bundled with Sanity
      return !bundledTypeNames.includes(typeName)
    })
    if (this.config.hiddenTypes) {
      // Exclude types which are explicitly named in hiddenTypes
      defaultTypes = defaultTypes.filter(typeName => {
        return !config.hiddenTypes.includes(typeName)
      })
    }
    return defaultTypes.filter(typeName => {
      const schemaType = this.getType(typeName)
      return schemaType.type !== null // this is the test for whether it is an toplevel type // todo: provide a better test
        && schemaType.type.name === 'object'
    })
  }

  getDisplayName(typeName) {
    const typeOption = this.config.typeOptions[typeName] || {}
    const type = this.getType(typeName)
    return typeOption.displayName || type.title || startCase(typeName)
  }

  // TODO: limit and offset is not yet implemented i gradient and only works partly because of a fluke
  // fix this when gql support limit, offset and order
  getListConstraints(typeName) {
    const typeOption = this.config.typeOptions[typeName]
    if (!typeOption) {
      return ''
    }
    const constraints = []
    if (typeOption.order) {
      // prefix order items with . because that's what gql requires
      //constraints.push(typeOptions.order)
    }
    if (typeOption.limit || typeOption.limit == 0) {
      constraints.push(`limit: ${typeOption.limit}`)
    }
    if (typeOption.offset) {
      constraints.push(`offset: ${typeOption.offset}`)
    }
    return constraints.filter(Boolean).join(', ')
  }

  getListQuery(options) {
    const {typeName, keyForId, keyForDisplayFieldName} = options
    const fieldName = this.getItemDisplayField(typeName)
    const constraints = this.getListConstraints(typeName)
    const selection = `"${keyForId}": _id, "${keyForDisplayFieldName}": ${fieldName}`
    return `${this.schema.name}.${typeName} [${constraints}] {${selection}}`
  }

}

export default DataAspectsResolver
<commit_msg>[data-aspects] Allow all types that are marked as json objects<commit_after>import config from 'config:@sanity/data-aspects'
import {startCase} from 'lodash'
const bundledTypeNames = ['geopoint', 'imageAsset', 'fileAsset']

class DataAspectsResolver {

  constructor(schema) {
    this.schema = schema
    this.config = Object.assign({typeOptions: {}}, config || {})
  }

  getConfig() {
    return this.config
  }

  getField(type, fieldName) {
    return type.fields.filter(currField => currField.name === fieldName)
  }

  getType(typeName) {
    return this.schema.get(typeName)
  }

  getInferredTypes() {
    let defaultTypes = this.schema.getTypeNames() || []
    defaultTypes = defaultTypes.filter(typeName => {
      // Exclude types which come bundled with Sanity
      return !bundledTypeNames.includes(typeName)
    })
    if (this.config.hiddenTypes) {
      // Exclude types which are explicitly named in hiddenTypes
      defaultTypes = defaultTypes.filter(typeName => {
        return !config.hiddenTypes.includes(typeName)
      })
    }
    return defaultTypes.filter(typeName => {
      const schemaType = this.getType(typeName)
      return schemaType.type !== null // this is the test for whether it is an toplevel type // todo: provide a better test
        && schemaType.jsonType === 'object'
    })
  }

  getDisplayName(typeName) {
    const typeOption = this.config.typeOptions[typeName] || {}
    const type = this.getType(typeName)
    return typeOption.displayName || type.title || startCase(typeName)
  }

  // TODO: limit and offset is not yet implemented i gradient and only works partly because of a fluke
  // fix this when gql support limit, offset and order
  getListConstraints(typeName) {
    const typeOption = this.config.typeOptions[typeName]
    if (!typeOption) {
      return ''
    }
    const constraints = []
    if (typeOption.order) {
      // prefix order items with . because that's what gql requires
      //constraints.push(typeOptions.order)
    }
    if (typeOption.limit || typeOption.limit == 0) {
      constraints.push(`limit: ${typeOption.limit}`)
    }
    if (typeOption.offset) {
      constraints.push(`offset: ${typeOption.offset}`)
    }
    return constraints.filter(Boolean).join(', ')
  }

  getListQuery(options) {
    const {typeName, keyForId, keyForDisplayFieldName} = options
    const fieldName = this.getItemDisplayField(typeName)
    const constraints = this.getListConstraints(typeName)
    const selection = `"${keyForId}": _id, "${keyForDisplayFieldName}": ${fieldName}`
    return `${this.schema.name}.${typeName} [${constraints}] {${selection}}`
  }

}

export default DataAspectsResolver
<|endoftext|>


<commit_before>import Ember from 'ember';

// isEmpty
const { isEmpty } = Ember;

/**
  Provides Current Order and Checkout Functionality to the Yebo service.  This
  mixin is applied to the Yebo service when yebo-ember-checkouts initializes,
  therefore all functionality described here is available like so:

  ```javascript
  this.yebo.addToCart(variantModel, 5);
  ```

  @class CurrentOrderSupport
  @namespace Mixin
  @extends Ember.Mixin
  */
export default Ember.Mixin.create({
  /**
    A generic event triggered whenever a Yebo Server request fails.

    @event serverError
    @param {Object} error The error object returned from the Yebo Server.
    */

  /**
    Triggered whenever a Line Item is created or updated.

    @event didAddToCart
    @param {DS.Model} lineItem The newly updated lineItem object
    */

  /**
    Triggered whenever a Line Item is created or updated.

    @event addToCartFailed
    @param {Error} error The returned Server Error.
    */

  /**
    Triggered whenever a new Order is created for the checkout user.

    @event didCreateNewOrder
    @param {DS.Model} order The newly created order object
    */

  /**
    Triggered whenever a new Order is created for the checkout user.

    @event newOrderCreateFailed
    @param {Object} error The returned Server Error.
    */

  /**
    Triggered whenever a new Order is created for the checkout user.

    @event didClearCurrentOrder
    */

  /**
    Triggered whenever the Current Order changes State.

    @event checkoutStateDidChange
    @param {DS.Model} order The Current Order.
    */

  /**
    Triggered when the `_saveCurrentOrder` call succeeds.

    @event didSaveCurrentOrder
    @param {Ember.RSVP.Promise} currentOrderPromise A promise that resolves to
    the Current Order
    */

  /**
    Triggered when the `_saveCurrentOrder` call fails.

    @event saveCurrentOrderFailed
    @param {Error} error The returned Server Error.
    */

  /**
    Triggered when the `advanceCurrentOrder` call succeeds.

    @event didAdvanceCurrentOrder
    @param {Ember.RSVP.Promise} currentOrderPromise A promise that resolves to
    the Current Order
    */

  /**
    Triggered when the `advanceCurrentOrder` call fails.

    @event advanceCurrentOrderFailed
    @param {Error} error The returned Server Error.
    */

  /**
    Triggered whenever the Current Order reached it's "Complete" State.

    @event currentOrderDidComplete
    @param {DS.Model} order The Current Order.
    */

  /**
    A method called in the `yebo-ember-checkouts` initializer after the
    `Checkouts` mixin is applied to the Yebo service, to initialize functionality
    in this mixin.

    @method _restoreCurrentOrder
    @private
    @return {Boolean} Always resolves to `true`.
    */
  _restoreCurrentOrder: function() {
    // Restore!
    this.restore();

    // Get the order storaged
    let orderId = this.get('orderId');

    // Return a promise
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Resolve here if no orderId
      if( !orderId )
        return resolve();

      // Check if the order has user
      YeboSDK.Store.fetch('cart/user', { number: orderId }, 'GET').then((res) => {
        // Initialize a cart that does not have an user
        if( !res.has_user ) {
          // Instanciante the cart
          this.instanciateCart().then(() => {
            // Resolve the main promise
            resolve();
          });
        } else
          resolve();
      });
    });
  },

  /**
   * This method is used to load the cart and the order based on
   * a data passed as argument and the stored info about it.
   *
   * @method
   * @public
   */
  instanciateCart: function(data) {
    // The authenticated value
    let authenticated = !isEmpty(data);

    // OrderId
    let orderId = this.get('orderId');

    // Check if its necessary to create the cart
    if( !orderId )
      return;

    // Create a new cart
    let cart;

    // The options used to get the current order order
    let orderOptions = {};

    if( authenticated ) {
      // Get the token
      let token = data.user ? data.user.token : data.token;

      // Create the cart
      cart = new YeboSDK.Cart(orderId, token);

      // Add the token to the orderOptions
      orderOptions.token = token;
    } else {
      cart = new YeboSDK.Cart(orderId);
    }

    // Return an Promise
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Get the cart order
      cart.order.then((res) => {
        // Find the order
        YeboSDK.Store.fetch(`orders/${res.number}`, orderOptions).then((orderRes) => {
          // Yebo Store
          let store = this.get('yebo').store;

          // Push the records to the ember
          store.pushPayload(orderRes);

          // Set the Cart
          this.set('currentCart', cart);

          // Set the current order
          this.set('currentOrder', store.peekRecord('order', res.number));

          // Resolve it
          resolve();
        }).catch((error) => {
          // Clean the local storage(persist)
          // ===================== WHATCH HERE ========================
          // TODO: This is wapping order if it timeout
          // this.persist({
          //   guestToken: null,
          //   orderId: null
          // });

          // Trigger the error
          this.trigger('serverError', error);

          // Reject it
          reject();
        });
      }).catch((error) => {
        // Clean the local storage(persist)
        this.persist({
          guestToken: null,
          orderId: null
        });

        // Trigger the error
        this.trigger('serverError', error);

        // Reject it
        reject();
      });
    });
  },

  /**
    The token used to Authenticate the current user against the current order.  Persisted
    to local storage via `yebo-ember-core/mixins/storable`.  This property is
    sent to the Yebo server via the header `X-Yebo-Order-Token`.

    @property guestToken
    @type String
    @readOnly
    @default null
    */
  guestToken: null,

  /**
    The user's Current Order number, persisted to local storage via
    `yebo-ember-core/mixins/storable`.  This property is sent to the Yebo
    server via the header `X-Yebo-Order-Id`.

    @property orderId
    @type String
    @readOnly
    @default null
    */
  orderId: null,

  /**
    A reference to the Current Order.  It is only set twice in this code,
    once on Application initialization (in the case it was persisted), and once
    when a new order is created through the internal method `_createNewOrder`.

    @property currentOrder
    @type DS.Model
    @default null
    */
  currentOrder: null,

  /**
   * Reference to the YeboSDK cart,
   * this can be used to manage the currentCart.
   * Actions like add, remove and update line items are related to this
   * instance
   *
   * @property currentCart
   * @type YeboSDK.Cart
   * @default null
   */
  currentCart: null,

  /**
    A reference to the Stateful Checkouts service.

    @property checkouts
    @type Ember.Service
    */
  checkouts: Ember.inject.service('checkouts'),

  /**
   *
   */
  yebo: Ember.inject.service(),

  /**
   *
   */
  sessionAccount: Ember.inject.service(),

  /**
    Adds a lineItem to the currentOrder. If there is no Current Order,
    Yebo Ember will request a new order from the server, and set it as the
    Current Order on the Yebo service.

    @method addToCart
    @param {DS.Model} variant A class of the variant model
    @param {Integer} quantity Optional, A quantity for the Line Item.
    @return {Ember.RSVP.Promise} A promise that resolves to the newly saved Line Item.
    */
  addToCart: function(variant, quantity) {
    // Check if the cart is already initialized
    let cart = this.get('currentCart');
    let order = this.get('currentOrder');

    // Not initialized!
    if (cart === null || order === null) {
      // Initialize the cart
      return this._initializeCart().then(() => {
        // Add it!
        return this._addToCart(variant, quantity);
      });
    } else {
      // Everything is ready
      // Add it!
      return this._addToCart(variant, quantity);
    }
  },

  /**
   * Remote lineItem from the cart
   * @method
   * @public
   * @param {DS.Model} lineItem The lineItem model that will be removed
   * @return {Ember.RSVP.Promise} A promise that resolves to the saved Line Item.
   */
  removeFromTheCart: function(lineItem, qty) {
    // Check if the cart is already initialized
    let cart = this.get('currentCart');
    let order = this.get('currentOrder');

    // The cart and order should be already initialized
    if( cart === null || order === null )
      return;

    // Remove it!
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Remove it using SDK
      cart.removeItem(lineItem.get('id'), qty).then((res) => {
        // Find the line item
        this.get('yebo.store').findRecord('lineItem', res.item.id).then((lineItem) => {
          // Reload the order
          order.reload();

          // Resolve it
          resolve(lineItem);
        });
      });
    });
  },

  /**
   * Empty the current cart
   */
  emptyCart: function() {
    // Current Cart
    let cart = this.get('currentCart');

    // Empty IT!!
    cart.empty().then(() => {
      // Update the order
      this.get('currentOrder').reload();
    });
  },

  /**
   * This method is used both to create an Order(currentOrder) for the other
   * sections of the application and the Cart(currentCart) that will be
   * used in the checkout area.
   *
   * @method _initializeCart
   * @private
   * in the initialization
   * @return {Ember.RSVP.Promise} A promise that resolves the cart initialized
   */
  _initializeCart() {
    // Start with the cart
    let cart = this.get('cart');

    // Initialize the cart
    cart = new YeboSDK.Cart(this.get('currentOrder.number'), this.get('sessionAccount.user.token'));
    // Put it in the instance
    this.set('currentCart', cart);

    return new Ember.RSVP.Promise((resolve, reject) => {
      // Find the cart order
      cart.order.then((response) => {
        // Check if the order is real
        if (response.real) {
          // Get the order
          this.get('yebo.store').find('order', response.number).then((order) => {
            // Set the order
            this.set('currentOrder', order);
            // Persist it to local storage
            this.persist({
              guestToken: order.get('guestToken'),
              orderId: order.get('id')
            });
            // Resolve the promise
            resolve(order);
          });
        } else {
          // The order does not exists!
          this.set('currentOrder', null);
        }
      }).catch(() => {
        this.clearCurrentOrder();

        reject();
      });
    });
  },

  /**
   * The method that really add to the cart
   * this was create to unify the method,
   * it does not matter how the currentCart and currentOrder were created.
   *
   * @method _addToCart
   * @private
   * @param {DS.Model} variant A class of the variant model
   * @param {Integer} quantity Optional, A quantity for the Line Item.
   * @return {Ember.RSVP.Promise} A promise that resolves to the newly saved Line Item.
   */
  _addToCart(variant, quantity) {
    // Get the two good guys
    let cart = this.get('currentCart');
    let order = this.get('currentOrder');

    // Add the variant to the cart using the SDK method
    return new Ember.RSVP.Promise((resolve, reject) => {
      cart.add(variant.get('id'), quantity).then((response) => {
        // Is the order real?
        // update order, but order might be huge
        // !this.get('currentOrder')
        if (response.order.real) {
          // Find it and set as the currentOrder
          // TODO: This response is huge, i can't do this in every request
          this.get('yebo.store').find('order', response.order.number).then((order) => {
            // Set the order
            this.set('currentOrder', order);
          });
        };
        // Find the line item
        this.get('yebo.store').findRecord('lineItem', response.item.id).then((lineItem) => {
          // Event
          this.trigger('didAddToCart', lineItem);
          // Finish!
          resolve(lineItem);
        }).catch((error) => {
          this.trigger('serverError', error);
          reject(error);
        });
      }).catch((error) => {
        this.trigger('serverError', error);
        reject(error);
      });
    });
  },

  /**
    An internal method for saving Line Items.  If it is called for a variant that
    is already in the current order, it will add to the corresponding Line Item's
    quantity, otherwise it will create a new Line Item for that variant.

    @method _saveLineItem
    @private
    @param {Ember.Object} variant A class of the variant model
    @param {Integer} quantity A quantity for the `lineItem`
    @param {Ember.Object} order The corresponding order
    @return {Ember.RSVP.Promise} A promise that resolves to the newly created or
    updated `lineItem` object.
    */
  _saveLineItem: function(variant, quantity, order) {
    var _this = this;
    var lineItem = order.get('lineItems').findBy('variant', variant);

    if (lineItem) {
      var currentQuantity = lineItem.get('quantity');
      lineItem.set('quantity', currentQuantity + quantity);
    } else {
      lineItem = this.store.createRecord('lineItem', {
        variant: variant,
        quantity: quantity
      });
    }

    return lineItem.save().then(
      function(lineItem) {
        _this.trigger('didAddToCart', lineItem);
        return lineItem;
      },
      function(error) {
        _this.trigger('serverError', error);
        return error;
      }
    );
  },

  /**
    Will attempt to create a new Order for the checkout user, and save the `orderId`
    and `guestToken` to the Yebo service, so that it will persist across page
    refreshes.  It will also initiate the state machine for the current order.

    @method _createNewOrder
    @private
    @return {Ember.RSVP.Promise} A promise that resolves to the newly created
    Yebo Order.
    */
  _createNewOrder: function() {
    return this.store.createRecord('order').save().then((newOrder)=> {
      this.set('currentOrder', newOrder);
      this.persist({
        guestToken: newOrder.get('guestToken'),
        orderId: newOrder.get('id')
      });
      this.trigger('didCreateNewOrder', newOrder);
      this.get('checkouts').transition(newOrder.get('state'));
      return newOrder;
    }, (error) => {
      this.trigger('newOrderCreateFailed', error);
      this.trigger('serverError', error);
      return error;
    }
                                                       );
  },

  /**
    Clears the current order and any reference to it.

    @method clearCurrentOrder
    @return {Boolean} Always returns `true`.
    */
  clearCurrentOrder: function(didComplete) {
    if (didComplete) {
      this.trigger('currentOrderDidComplete', this.get('currentOrder'));
    }
    this.persist({
      guestToken: null,
      orderId: null
    });
    this.set('currentOrder', null);
    this.set('currentCart', null);
    // this.get('checkouts').transition();
    this.trigger('didClearCurrentOrder');
    return true;
  }
});
<commit_msg>Trigger an event when the order is loaded<commit_after>import Ember from 'ember';

// isEmpty
const { isEmpty } = Ember;

/**
  Provides Current Order and Checkout Functionality to the Yebo service.  This
  mixin is applied to the Yebo service when yebo-ember-checkouts initializes,
  therefore all functionality described here is available like so:

  ```javascript
  this.yebo.addToCart(variantModel, 5);
  ```

  @class CurrentOrderSupport
  @namespace Mixin
  @extends Ember.Mixin
  */
export default Ember.Mixin.create({
  /**
    A generic event triggered whenever a Yebo Server request fails.

    @event serverError
    @param {Object} error The error object returned from the Yebo Server.
    */

  /**
    Triggered whenever a Line Item is created or updated.

    @event didAddToCart
    @param {DS.Model} lineItem The newly updated lineItem object
    */

  /**
    Triggered whenever a Line Item is created or updated.

    @event addToCartFailed
    @param {Error} error The returned Server Error.
    */

  /**
    Triggered whenever a new Order is created for the checkout user.

    @event didCreateNewOrder
    @param {DS.Model} order The newly created order object
    */

  /**
    Triggered whenever a new Order is created for the checkout user.

    @event newOrderCreateFailed
    @param {Object} error The returned Server Error.
    */

  /**
    Triggered whenever a new Order is created for the checkout user.

    @event didClearCurrentOrder
    */

  /**
    Triggered whenever the Current Order changes State.

    @event checkoutStateDidChange
    @param {DS.Model} order The Current Order.
    */

  /**
    Triggered when the `_saveCurrentOrder` call succeeds.

    @event didSaveCurrentOrder
    @param {Ember.RSVP.Promise} currentOrderPromise A promise that resolves to
    the Current Order
    */

  /**
    Triggered when the `_saveCurrentOrder` call fails.

    @event saveCurrentOrderFailed
    @param {Error} error The returned Server Error.
    */

  /**
    Triggered when the `advanceCurrentOrder` call succeeds.

    @event didAdvanceCurrentOrder
    @param {Ember.RSVP.Promise} currentOrderPromise A promise that resolves to
    the Current Order
    */

  /**
    Triggered when the `advanceCurrentOrder` call fails.

    @event advanceCurrentOrderFailed
    @param {Error} error The returned Server Error.
    */

  /**
    Triggered whenever the Current Order reached it's "Complete" State.

    @event currentOrderDidComplete
    @param {DS.Model} order The Current Order.
    */

  /**
    A method called in the `yebo-ember-checkouts` initializer after the
    `Checkouts` mixin is applied to the Yebo service, to initialize functionality
    in this mixin.

    @method _restoreCurrentOrder
    @private
    @return {Boolean} Always resolves to `true`.
    */
  _restoreCurrentOrder: function() {
    // Restore!
    this.restore();

    // Get the order storaged
    let orderId = this.get('orderId');

    // Return a promise
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Resolve here if no orderId
      if( !orderId )
        return resolve();

      // Check if the order has user
      YeboSDK.Store.fetch('cart/user', { number: orderId }, 'GET').then((res) => {
        // Initialize a cart that does not have an user
        if( !res.has_user ) {
          // Instanciante the cart
          this.instanciateCart().then(() => {
            // Resolve the main promise
            resolve();
          });
        } else
          resolve();
      });
    });
  },

  /**
   * This method is used to load the cart and the order based on
   * a data passed as argument and the stored info about it.
   *
   * @method
   * @public
   */
  instanciateCart: function(data) {
    // The authenticated value
    let authenticated = !isEmpty(data);

    // OrderId
    let orderId = this.get('orderId');

    // Check if its necessary to create the cart
    if( !orderId )
      return;

    // Create a new cart
    let cart;

    // The options used to get the current order order
    let orderOptions = {};

    if( authenticated ) {
      // Get the token
      let token = data.user ? data.user.token : data.token;

      // Create the cart
      cart = new YeboSDK.Cart(orderId, token);

      // Add the token to the orderOptions
      orderOptions.token = token;
    } else {
      cart = new YeboSDK.Cart(orderId);
    }

    // Return an Promise
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Get the cart order
      cart.order.then((res) => {
        // Find the order
        YeboSDK.Store.fetch(`orders/${res.number}`, orderOptions).then((orderRes) => {
          // Yebo Store
          let store = this.get('yebo').store;

          // Push the records to the ember
          store.pushPayload(orderRes);

          // Set the Cart
          this.set('currentCart', cart);

          // Set the current order
          this.set('currentOrder', store.peekRecord('order', res.number));

          // Order loaded event
          this.get('yebo').trigger('orderLoaded');

          // Resolve it
          resolve();
        }).catch((error) => {
          // Clean the local storage(persist)
          // ===================== WHATCH HERE ========================
          // TODO: This is wapping order if it timeout
          // this.persist({
          //   guestToken: null,
          //   orderId: null
          // });

          // Trigger the error
          this.trigger('serverError', error);

          // Reject it
          reject();
        });
      }).catch((error) => {
        // Clean the local storage(persist)
        this.persist({
          guestToken: null,
          orderId: null
        });

        // Trigger the error
        this.trigger('serverError', error);

        // Reject it
        reject();
      });
    });
  },

  /**
    The token used to Authenticate the current user against the current order.  Persisted
    to local storage via `yebo-ember-core/mixins/storable`.  This property is
    sent to the Yebo server via the header `X-Yebo-Order-Token`.

    @property guestToken
    @type String
    @readOnly
    @default null
    */
  guestToken: null,

  /**
    The user's Current Order number, persisted to local storage via
    `yebo-ember-core/mixins/storable`.  This property is sent to the Yebo
    server via the header `X-Yebo-Order-Id`.

    @property orderId
    @type String
    @readOnly
    @default null
    */
  orderId: null,

  /**
    A reference to the Current Order.  It is only set twice in this code,
    once on Application initialization (in the case it was persisted), and once
    when a new order is created through the internal method `_createNewOrder`.

    @property currentOrder
    @type DS.Model
    @default null
    */
  currentOrder: null,

  /**
   * Reference to the YeboSDK cart,
   * this can be used to manage the currentCart.
   * Actions like add, remove and update line items are related to this
   * instance
   *
   * @property currentCart
   * @type YeboSDK.Cart
   * @default null
   */
  currentCart: null,

  /**
    A reference to the Stateful Checkouts service.

    @property checkouts
    @type Ember.Service
    */
  checkouts: Ember.inject.service('checkouts'),

  /**
   *
   */
  yebo: Ember.inject.service(),

  /**
   *
   */
  sessionAccount: Ember.inject.service(),

  /**
    Adds a lineItem to the currentOrder. If there is no Current Order,
    Yebo Ember will request a new order from the server, and set it as the
    Current Order on the Yebo service.

    @method addToCart
    @param {DS.Model} variant A class of the variant model
    @param {Integer} quantity Optional, A quantity for the Line Item.
    @return {Ember.RSVP.Promise} A promise that resolves to the newly saved Line Item.
    */
  addToCart: function(variant, quantity) {
    // Check if the cart is already initialized
    let cart = this.get('currentCart');
    let order = this.get('currentOrder');

    // Not initialized!
    if (cart === null || order === null) {
      // Initialize the cart
      return this._initializeCart().then(() => {
        // Add it!
        return this._addToCart(variant, quantity);
      });
    } else {
      // Everything is ready
      // Add it!
      return this._addToCart(variant, quantity);
    }
  },

  /**
   * Remote lineItem from the cart
   * @method
   * @public
   * @param {DS.Model} lineItem The lineItem model that will be removed
   * @return {Ember.RSVP.Promise} A promise that resolves to the saved Line Item.
   */
  removeFromTheCart: function(lineItem, qty) {
    // Check if the cart is already initialized
    let cart = this.get('currentCart');
    let order = this.get('currentOrder');

    // The cart and order should be already initialized
    if( cart === null || order === null )
      return;

    // Remove it!
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Remove it using SDK
      cart.removeItem(lineItem.get('id'), qty).then((res) => {
        // Find the line item
        this.get('yebo.store').findRecord('lineItem', res.item.id).then((lineItem) => {
          // Reload the order
          order.reload();

          // Resolve it
          resolve(lineItem);
        });
      });
    });
  },

  /**
   * Empty the current cart
   */
  emptyCart: function() {
    // Current Cart
    let cart = this.get('currentCart');

    // Empty IT!!
    cart.empty().then(() => {
      // Update the order
      this.get('currentOrder').reload();
    });
  },

  /**
   * This method is used both to create an Order(currentOrder) for the other
   * sections of the application and the Cart(currentCart) that will be
   * used in the checkout area.
   *
   * @method _initializeCart
   * @private
   * in the initialization
   * @return {Ember.RSVP.Promise} A promise that resolves the cart initialized
   */
  _initializeCart() {
    // Start with the cart
    let cart = this.get('cart');

    // Initialize the cart
    cart = new YeboSDK.Cart(this.get('currentOrder.number'), this.get('sessionAccount.user.token'));
    // Put it in the instance
    this.set('currentCart', cart);

    return new Ember.RSVP.Promise((resolve, reject) => {
      // Find the cart order
      cart.order.then((response) => {
        // Check if the order is real
        if (response.real) {
          // Get the order
          this.get('yebo.store').find('order', response.number).then((order) => {
            // Set the order
            this.set('currentOrder', order);
            // Persist it to local storage
            this.persist({
              guestToken: order.get('guestToken'),
              orderId: order.get('id')
            });
            // Resolve the promise
            resolve(order);
          });
        } else {
          // The order does not exists!
          this.set('currentOrder', null);
        }
      }).catch(() => {
        this.clearCurrentOrder();

        reject();
      });
    });
  },

  /**
   * The method that really add to the cart
   * this was create to unify the method,
   * it does not matter how the currentCart and currentOrder were created.
   *
   * @method _addToCart
   * @private
   * @param {DS.Model} variant A class of the variant model
   * @param {Integer} quantity Optional, A quantity for the Line Item.
   * @return {Ember.RSVP.Promise} A promise that resolves to the newly saved Line Item.
   */
  _addToCart(variant, quantity) {
    // Get the two good guys
    let cart = this.get('currentCart');
    let order = this.get('currentOrder');

    // Add the variant to the cart using the SDK method
    return new Ember.RSVP.Promise((resolve, reject) => {
      cart.add(variant.get('id'), quantity).then((response) => {
        // Is the order real?
        // update order, but order might be huge
        // !this.get('currentOrder')
        if (response.order.real) {
          // Find it and set as the currentOrder
          // TODO: This response is huge, i can't do this in every request
          this.get('yebo.store').find('order', response.order.number).then((order) => {
            // Set the order
            this.set('currentOrder', order);
          });
        };
        // Find the line item
        this.get('yebo.store').findRecord('lineItem', response.item.id).then((lineItem) => {
          // Event
          this.trigger('didAddToCart', lineItem);
          // Finish!
          resolve(lineItem);
        }).catch((error) => {
          this.trigger('serverError', error);
          reject(error);
        });
      }).catch((error) => {
        this.trigger('serverError', error);
        reject(error);
      });
    });
  },

  /**
    An internal method for saving Line Items.  If it is called for a variant that
    is already in the current order, it will add to the corresponding Line Item's
    quantity, otherwise it will create a new Line Item for that variant.

    @method _saveLineItem
    @private
    @param {Ember.Object} variant A class of the variant model
    @param {Integer} quantity A quantity for the `lineItem`
    @param {Ember.Object} order The corresponding order
    @return {Ember.RSVP.Promise} A promise that resolves to the newly created or
    updated `lineItem` object.
    */
  _saveLineItem: function(variant, quantity, order) {
    var _this = this;
    var lineItem = order.get('lineItems').findBy('variant', variant);

    if (lineItem) {
      var currentQuantity = lineItem.get('quantity');
      lineItem.set('quantity', currentQuantity + quantity);
    } else {
      lineItem = this.store.createRecord('lineItem', {
        variant: variant,
        quantity: quantity
      });
    }

    return lineItem.save().then(
      function(lineItem) {
        _this.trigger('didAddToCart', lineItem);
        return lineItem;
      },
      function(error) {
        _this.trigger('serverError', error);
        return error;
      }
    );
  },

  /**
    Will attempt to create a new Order for the checkout user, and save the `orderId`
    and `guestToken` to the Yebo service, so that it will persist across page
    refreshes.  It will also initiate the state machine for the current order.

    @method _createNewOrder
    @private
    @return {Ember.RSVP.Promise} A promise that resolves to the newly created
    Yebo Order.
    */
  _createNewOrder: function() {
    return this.store.createRecord('order').save().then((newOrder)=> {
      this.set('currentOrder', newOrder);
      this.persist({
        guestToken: newOrder.get('guestToken'),
        orderId: newOrder.get('id')
      });
      this.trigger('didCreateNewOrder', newOrder);
      this.get('checkouts').transition(newOrder.get('state'));
      return newOrder;
    }, (error) => {
      this.trigger('newOrderCreateFailed', error);
      this.trigger('serverError', error);
      return error;
    }
                                                       );
  },

  /**
    Clears the current order and any reference to it.

    @method clearCurrentOrder
    @return {Boolean} Always returns `true`.
    */
  clearCurrentOrder: function(didComplete) {
    if (didComplete) {
      this.trigger('currentOrderDidComplete', this.get('currentOrder'));
    }
    this.persist({
      guestToken: null,
      orderId: null
    });
    this.set('currentOrder', null);
    this.set('currentCart', null);
    // this.get('checkouts').transition();
    this.trigger('didClearCurrentOrder');
    return true;
  }
});
<|endoftext|>


<commit_before>import React from 'react';
import { c } from 'ttag';
import { NEWS } from 'proton-shared/lib/constants';
import { updateNews } from 'proton-shared/lib/api/settings';
import { toggleBit, hasBit } from 'proton-shared/lib/helpers/bitset';

import { Checkbox } from '../../components';
import { useUserSettings, useEventManager, useNotifications, useApi, useLoading } from '../../hooks';

const { ANNOUNCEMENTS, FEATURES, NEWSLETTER, BETA, BUSINESS } = NEWS;

const NewsCheckboxes = () => {
    const [{ News } = {}] = useUserSettings();
    const { createNotification } = useNotifications();
    const { call } = useEventManager();
    const api = useApi();
    const [loading, withLoading] = useLoading();

    const update = async (mask) => {
        await api(updateNews(toggleBit(News, mask)));
        await call();
        createNotification({ text: c('Info').t`Emailing preference updated` });
    };

    const handleChange = (mask) => () => withLoading(update(mask));

    const checkboxes = [
        {
            id: 'announcements',
            flag: ANNOUNCEMENTS,
            text: c('Label for news').t`Proton announcements (2-3 emails per year)`,
        },
        { id: 'features', flag: FEATURES, text: c('Label for news').t`Proton major features (3-4 emails per year)` },
        { id: 'business', flag: BUSINESS, text: c('Label for news').t`Proton for business (4-5 emails per year)` },
        { id: 'newsletter', flag: NEWSLETTER, text: c('Label for news').t`Proton newsletter (8-10 emails per year)` },
        { id: 'beta', flag: BETA, text: c('Label for news').t`Proton beta (10-12 emails per year)` },
    ];

    return (

            {checkboxes.map(({ id, flag, text }) => {
                return (

                        <Checkbox checked={hasBit(News, flag)} disabled={loading} onChange={handleChange(flag)}>
                            {text}
                        </Checkbox>

                );
            })}

    );
};

export default NewsCheckboxes;
<commit_msg>Fix typo Proton Beta<commit_after>import React from 'react';
import { c } from 'ttag';
import { NEWS } from 'proton-shared/lib/constants';
import { updateNews } from 'proton-shared/lib/api/settings';
import { toggleBit, hasBit } from 'proton-shared/lib/helpers/bitset';

import { Checkbox } from '../../components';
import { useUserSettings, useEventManager, useNotifications, useApi, useLoading } from '../../hooks';

const { ANNOUNCEMENTS, FEATURES, NEWSLETTER, BETA, BUSINESS } = NEWS;

const NewsCheckboxes = () => {
    const [{ News } = {}] = useUserSettings();
    const { createNotification } = useNotifications();
    const { call } = useEventManager();
    const api = useApi();
    const [loading, withLoading] = useLoading();

    const update = async (mask) => {
        await api(updateNews(toggleBit(News, mask)));
        await call();
        createNotification({ text: c('Info').t`Emailing preference updated` });
    };

    const handleChange = (mask) => () => withLoading(update(mask));

    const checkboxes = [
        {
            id: 'announcements',
            flag: ANNOUNCEMENTS,
            text: c('Label for news').t`Proton announcements (2-3 emails per year)`,
        },
        { id: 'features', flag: FEATURES, text: c('Label for news').t`Proton major features (3-4 emails per year)` },
        { id: 'business', flag: BUSINESS, text: c('Label for news').t`Proton for business (4-5 emails per year)` },
        { id: 'newsletter', flag: NEWSLETTER, text: c('Label for news').t`Proton newsletter (8-10 emails per year)` },
        { id: 'beta', flag: BETA, text: c('Label for news').t`Proton Beta (10-12 emails per year)` },
    ];

    return (

            {checkboxes.map(({ id, flag, text }) => {
                return (

                        <Checkbox checked={hasBit(News, flag)} disabled={loading} onChange={handleChange(flag)}>
                            {text}
                        </Checkbox>

                );
            })}

    );
};

export default NewsCheckboxes;
<|endoftext|>


<commit_before>/* Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
   You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   either express or implied. See the License for the specific language governing permissions and limitations under the License.*/
// ruler class

define.class('$ui/view', function(require, $ui$, view, label, icon){
	// The foldcontainer shows/hides all its children when the top bar is clicked

	this.position = "relative"
	this.borderwidth = 1
	this.borderradius = 1
	this.margin = 0
	this.bg = 0
	this.alignitems = "stretch"
	this.bordercolor = vec4("#c0c0c0")
	this.padding = 0
	this.flexdirection = "column"

	this.attributes = {
		// The current state of the foldcontainer. false = open, Ttue = closed.
		collapsed: Config({type:boolean, value: false, persist:true}),
		// The icon to use in the top left of the foldcontainer. See the FontAwesome cheatsheet for acceptable icon names.
		icon: 'times',
		title: 'folding thing',
		// The main color from which the foldcontainer will build some gradients.
		basecolor: Config({type:vec4, value: vec4("#8080c0")}),
		fontsize: Config({type:float, value: 12}),
		autogradient: Config({type:boolean, value: false})
	}

	// Function to change the open/closed state. Used by the click handler of the clickablebar.
	this.toggle = function(){
		this.collapsed = !this.collapsed;
	}

	// subclass to lay out the clickable portion of the folding container
	define.class(this, 'clickablebar', view, function(){

		this.bggradient = function(a,b){
			var fill = mix(col1, col2,  (a.y)/0.8);
			return fill;
		}

		// default click-handler - when not bound this write "nothing happens" to the console.
		this.toggle = function(){console.log("nothing happens")}

		this.attributes = {
			title: Config({type:String}),
			icon: Config({type:String, value:""}),
			col1: Config({value:vec4(0,0,0,0),persist:true, meta:"color", motion:"linear", duration:0.1}),
			col2: Config({value:vec4(0,0,0,0),persist:true, meta:"color", motion:"linear", duration:0.14}),
			collapsed: false
		}
		this.position = "relative";

		this.bg = {
			color: function(){
				var fill = mix(view.col1, view.col2,  (mesh.y)/0.8)
				return fill;
			}
		}

		this.padding = 3
		this.justifycontent=  "space-between"
		this.alignitems = "flex-start"
		this.flex = 1

		// The clickable bar creates icon and a textfield children.
		this.render = function(){
			var res = []

			if (this.icon)res.push(icon({fontsize:this.outer.fontsize, icon:this.icon, fgcolor:vec4.contrastcolor(this.outer.basecolor) }));
			if (this.title) res.push(label({font: require('$resources/fonts/opensans_bold_ascii.glf'),marginleft:5,fgcolor:vec4.contrastcolor(this.outer.basecolor), fontsize: this.outer.fontsize, text:this.title, bg:0 }));
			var res2 = [view({bg:0},res), icon({fontsize:this.outer.fontsize,alignself:"flex-end", icon:this.collapsed? "chevron-right":"chevron-down", fgcolor:vec4.contrastcolor(this.outer.basecolor) })]
			return res2;
		}

		this.statedefault = function(first){
			//console.log(this.parent.basecolor)
			this.col1 = Mark(vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.0), first)
			if (this.autogradient) {
				this.col2 = Mark(vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.2), first)
			}
			else{
				this.col2 = this.col1;
			}
		}

		this.stateover = function(){
			this.col1 = Mark(vec4.vec4_mul_float32_rgb(vec4(this.parent.basecolor), 1.2))
			if (this._autogradient){
				this.col2 = vec4.vec4_mul_float32_rgb(vec4(this.parent.basecolor), 1.1)
			}
			else{
				this.col2 = this.col1;
			}
		}

		this.stateclick = function(){
			this.col1 = vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.3)
			if (this._autogradient){
				this.col2 = vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.0)
			}
			else{
				this.col2 = this.col1;
			}
			this.outer.toggle()
		}

		this.layout  = function(){this.statedefault();};
		this.init = function(){
			//	console.log("HERE",this.bgcolor)
			this.statedefault(true)
		}

		this.pointerover = this.stateover
		this.pointerout = this.statedefault
		this.pointerstart = this.stateclick
		this.pointerend = this.statedefault
	})

	// the main container view
	define.class(this, 'containerview', function(view){
		this.bg = {
			color:function(){
				return mix(view.bgcolor*1.7, vec4("white"), (mesh.y/8))
			}
		};
		this.bg = 0;
		this.padding = vec4(0,0,0,0),
		this.margin = vec4(0,0,0,0),
		this.position = "relative"
	})

	this.render = function(){

		this.bar = this.clickablebar({
			bgcolor:vec4("#3c3c3c"),
			borderwidth: this.borderwidth,
			bordercolor: this.bordercolor,
			icon: this.icon?this.icon:"",
			title: this.title,
			fontsize: this.fontsize,
			collapsed: this.collapsed
		});

		this.bar.pointertap = this.toggle.bind(this);
		var res = [this.bar];

		if (this.collapsed == false) {
			this.container = this.containerview({
				bgcolor: this.basecolor,
				borderwidth: this.borderwidth,
				bordercolor:this.bordercolor,

			},
			this.constructor_children)
			res.push(this.container)
		}
		this.children = [];

		return res;
	}

	var foldcontainer = this.constructor;

	this.constructor.examples = {
		BasicExample:function(){
			return [
				foldcontainer({icon:"flask", title:"folding thing", basecolor: "#90c0f0" } ,
					label({text:"I can be folded away!", fgcolor:"black", bgcolor:"transparent", margin:vec4(10) })
				)
			]
		}
	}
});
<commit_msg>Fold container event fix<commit_after>/* Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
   You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   either express or implied. See the License for the specific language governing permissions and limitations under the License.*/
// ruler class

define.class('$ui/view', function(require, $ui$, view, label, icon){
	// The foldcontainer shows/hides all its children when the top bar is clicked

	this.position = "relative"
	this.borderwidth = 1
	this.borderradius = 1
	this.margin = 0
	this.bg = 0
	this.alignitems = "stretch"
	this.bordercolor = vec4("#c0c0c0")
	this.padding = 0
	this.flexdirection = "column"

	this.attributes = {
		// The current state of the foldcontainer. false = open, Ttue = closed.
		collapsed: Config({type:boolean, value: false, persist:true}),
		// The icon to use in the top left of the foldcontainer. See the FontAwesome cheatsheet for acceptable icon names.
		icon: 'times',
		title: 'folding thing',
		// The main color from which the foldcontainer will build some gradients.
		basecolor: Config({type:vec4, value: vec4("#8080c0")}),
		fontsize: Config({type:float, value: 12}),
		autogradient: Config({type:boolean, value: false})
	}

	// Function to change the open/closed state. Used by the click handler of the clickablebar.
	this.toggle = function(){
		this.collapsed = !this.collapsed;
	}

	// subclass to lay out the clickable portion of the folding container
	define.class(this, 'clickablebar', view, function(){

		this.bggradient = function(a,b){
			var fill = mix(col1, col2,  (a.y)/0.8);
			return fill;
		}

		// default click-handler - when not bound this write "nothing happens" to the console.
		this.toggle = function(){console.log("nothing happens")}

		this.attributes = {
			title: Config({type:String}),
			icon: Config({type:String, value:""}),
			col1: Config({value:vec4(0,0,0,0),persist:true, meta:"color", motion:"linear", duration:0.1}),
			col2: Config({value:vec4(0,0,0,0),persist:true, meta:"color", motion:"linear", duration:0.14}),
			collapsed: false
		}
		this.position = "relative";

		this.bg = {
			color: function(){
				var fill = mix(view.col1, view.col2,  (mesh.y)/0.8)
				return fill;
			}
		}

		this.padding = 3
		this.justifycontent=  "space-between"
		this.alignitems = "flex-start"
		this.flex = 1

		// The clickable bar creates icon and a textfield children.
		this.render = function(){
			var res = []

			if (this.icon)res.push(icon({fontsize:this.outer.fontsize, icon:this.icon, fgcolor:vec4.contrastcolor(this.outer.basecolor) }));
			if (this.title) res.push(label({font: require('$resources/fonts/opensans_bold_ascii.glf'),marginleft:5,fgcolor:vec4.contrastcolor(this.outer.basecolor), fontsize: this.outer.fontsize, text:this.title, bg:0 }));
			var res2 = [view({bg:0},res), icon({fontsize:this.outer.fontsize,alignself:"flex-end", icon:this.collapsed? "chevron-right":"chevron-down", fgcolor:vec4.contrastcolor(this.outer.basecolor) })]
			return res2;
		}

		this.statedefault = function(first){
			//console.log(this.parent.basecolor)
			this.col1 = Mark(vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.0), first)
			if (this.autogradient) {
				this.col2 = Mark(vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.2), first)
			}
			else{
				this.col2 = this.col1;
			}
		}

		this.stateover = function(){
			this.col1 = Mark(vec4.vec4_mul_float32_rgb(vec4(this.parent.basecolor), 1.2))
			if (this._autogradient){
				this.col2 = vec4.vec4_mul_float32_rgb(vec4(this.parent.basecolor), 1.1)
			}
			else{
				this.col2 = this.col1;
			}
		}

		this.stateclick = function(){
			this.col1 = vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.3)
			if (this._autogradient){
				this.col2 = vec4.vec4_mul_float32(vec4(this.parent.basecolor), 1.0)
			}
			else{
				this.col2 = this.col1;
			}
			this.outer.toggle()
		}

		this.layout  = function(){this.statedefault();};
		this.init = function(){
			//	console.log("HERE",this.bgcolor)
			this.statedefault(true)
		}

		this.pointerover = this.stateover
		this.pointerout = this.statedefault
		this.pointerstart = function(ev){
			this.stateclick()
			console.log(ev)
		}
		this.pointerend = this.statedefault
	})

	// the main container view
	define.class(this, 'containerview', function(view){
		this.bg = {
			color:function(){
				return mix(view.bgcolor*1.7, vec4("white"), (mesh.y/8))
			}
		};
		this.bg = 0;
		this.padding = vec4(0,0,0,0),
		this.margin = vec4(0,0,0,0),
		this.position = "relative"
	})

	this.render = function(){

		this.bar = this.clickablebar({
			bgcolor:vec4("#3c3c3c"),
			borderwidth: this.borderwidth,
			bordercolor: this.bordercolor,
			icon: this.icon?this.icon:"",
			title: this.title,
			fontsize: this.fontsize,
			collapsed: this.collapsed
		});

		//this.bar.pointertap = this.toggle.bind(this);
		var res = [this.bar];

		if (this.collapsed == false) {
			this.container = this.containerview({
				bgcolor: this.basecolor,
				borderwidth: this.borderwidth,
				bordercolor:this.bordercolor,

			},
			this.constructor_children)
			res.push(this.container)
		}
		this.children = [];

		return res;
	}

	var foldcontainer = this.constructor;

	this.constructor.examples = {
		BasicExample:function(){
			return [
				foldcontainer({icon:"flask", title:"folding thing", basecolor: "#90c0f0" } ,
					label({text:"I can be folded away!", fgcolor:"black", bgcolor:"transparent", margin:vec4(10) })
				)
			]
		}
	}
});
<|endoftext|>


<commit_before>import $ from 'jquery';
import _ from 'underscore';

export function fetchNews() {
    return dispatch => {
        dispatch(requestNews());

        return $.ajax('/api/news')
            .done(function(data) {
                dispatch(receiveNews(data));
            });
    };
}

export function requestNews() {
    return {
        type: 'REQUEST_NEWS'
    };
}

export function receiveNews(news) {
    return {
        type: 'RECEIVE_NEWS',
        news: news
    };
}

export function loadNews(options) {
    let params = {};

    if(options && options.limit) {
        params.limit = options.limit;
    }

    return {
        types: ['REQUEST_NEWS', 'RECEIVE_NEWS'],
        shouldCallAPI: (state) => {
            return _.size(state.news.news) === 0 || (options && !!options.forceLoad);
        },
        APIParams: { url: '/api/news/', cache: false, data: params }
    };
}

export function addNews(newsText) {
    return {
        types: ['ADD_NEWS', 'NEWS_ADDED'],
        shouldCallAPI: (state) => {
            return state.news.news;
        },
        APIParams: {
            url: '/api/news',
            type: 'POST',
            data: { text: newsText }
        }
    };
}

export function saveNews(id, text) {
    return {
        types: ['SAVE_NEWS', 'NEWS_SAVED'],
        shouldCallAPI: () => true,
        APIParams: {
            url: `/api/news/${id}`,
            type: 'PUT',
            data: { text: text }
        }
    };
}

export function deleteNews(id) {
    return {
        types: ['DELETE_NEWS', 'NEWS_DELETED'],
        shouldCallAPI: () => true,
        APIParams: {
            url: `/api/news/${id}`,
            type: 'DELETE'
        }
    };
}

export function clearNewsStatus() {
    return {
        type: 'CLEAR_NEWS_STATUS'
    };
}
<commit_msg>Jsonify the news before calling the api endpoint<commit_after>import $ from 'jquery';
import _ from 'underscore';

export function fetchNews() {
    return dispatch => {
        dispatch(requestNews());

        return $.ajax('/api/news')
            .done(function(data) {
                dispatch(receiveNews(data));
            });
    };
}

export function requestNews() {
    return {
        type: 'REQUEST_NEWS'
    };
}

export function receiveNews(news) {
    return {
        type: 'RECEIVE_NEWS',
        news: news
    };
}

export function loadNews(options) {
    let params = {};

    if(options && options.limit) {
        params.limit = options.limit;
    }

    return {
        types: ['REQUEST_NEWS', 'RECEIVE_NEWS'],
        shouldCallAPI: (state) => {
            return _.size(state.news.news) === 0 || (options && !!options.forceLoad);
        },
        APIParams: { url: '/api/news/', cache: false, data: params }
    };
}

export function addNews(newsText) {
    return {
        types: ['ADD_NEWS', 'NEWS_ADDED'],
        shouldCallAPI: (state) => {
            return state.news.news;
        },
        APIParams: {
            url: '/api/news',
            type: 'POST',
            data: JSON.stringify({ text: newsText })
        }
    };
}

export function saveNews(id, text) {
    return {
        types: ['SAVE_NEWS', 'NEWS_SAVED'],
        shouldCallAPI: () => true,
        APIParams: {
            url: `/api/news/${id}`,
            type: 'PUT',
            data: { text: text }
        }
    };
}

export function deleteNews(id) {
    return {
        types: ['DELETE_NEWS', 'NEWS_DELETED'],
        shouldCallAPI: () => true,
        APIParams: {
            url: `/api/news/${id}`,
            type: 'DELETE'
        }
    };
}

export function clearNewsStatus() {
    return {
        type: 'CLEAR_NEWS_STATUS'
    };
}
<|endoftext|>


<commit_before>import {
  FETCH_INTERFACE_DATA
} from '../constants/action-types.js';

export function fetchAuditIcons () {
  const data = [{
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
    date: '2015-11-11 13:00:15',
    features: '3'
  }, {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
    date: '2015-11-11 13:00:15',
    features: '3'
  }, {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sidney No. 1 Lake Park',
    date: '2015-11-11 13:00:15',
    features: '3'
  }]

  return {
    type: FETCH_INTERFACE_DATA,
    payload: data
  };
}
<commit_msg>删除多余action文件<commit_after><|endoftext|>


<commit_before>angular.module('browse.ctrl', ['services.user', 'services.server', 'services.browse'])

.controller('BrowseController', ['$scope', '$http', '$state', '$timeout', 'User', 'Server', 'Browse',  function($scope, $http, $state, $timeout, User, Server, Browse){
  $scope.mapData = [];

  $scope.showSigninMsg = false;
  $scope.isLoggedIn = User.isLoggedIn;

  $scope.addTotalNodesOfMaps = Browse.addTotalNodesOfMaps;
  $scope.getMapData = Browse.getMapData;
  $scope.addMapToEmbarked = Browse.addMapToEmbarked;
  $scope.goToMap = Browse.goToMap;

  $scope.getMapData(function(mapData){
    $scope.mapData = mapData;
  });

  $scope.goToDash = function () {
      $state.go('dashboard');
  }

}]);
<commit_msg>(refactor) Browse page now sets current roadmap with query params<commit_after>angular.module('browse.ctrl', ['services.user', 'services.server', 'services.browse'])

.controller('BrowseController', ['$scope', '$http', '$state', '$timeout', 'User', 'Server', 'Browse',  function($scope, $http, $state, $timeout, User, Server, Browse){
  $scope.mapData = [];

  $scope.showSigninMsg = false;
  $scope.isLoggedIn = User.isLoggedIn;

  $scope.addTotalNodesOfMaps = Browse.addTotalNodesOfMaps;
  $scope.getMapData = Browse.getMapData;
  $scope.addMapToEmbarked = Browse.addMapToEmbarked;
  $scope.goToMap = Browse.goToMap;

  $scope.getMapData(function(mapData){
    $scope.mapData = mapData;
  });

  $scope.goToDash = function () {
      $state.go('dashboard');

  $scope.goToMap = function (mapID){
    $state.go('home.roadmapTemplate', { 'roadmapID': mapID });
  };

}]);
<|endoftext|>


<commit_before>import Ember from "ember";

export default Ember.Route.extend({
  ajax: Ember.inject.service(),
  user: Ember.inject.service(),
  errorHandler: Ember.inject.service(),
  toast: Ember.inject.service(),

  useLocation: false,
  locationId: null,
  latitude: null,
  longitude: null,
  accuracy: null,

  queryParams: {
    readHumanId: {
      refreshModel: true,
      as: 'human'
    },

    readZombieId: {
      refreshModel: true,
      as: 'zombie'
    }
  },

  model(params) {
    if (this.get('user').isLoggedIn()) {
      return this.get('user').getUserProfile().then((result) => {
        var profile = result.profile;
        if (profile.team === 'zombie') {
          return {humanId: params.readHumanId, zombieId: profile.zombieId};
        }
        else {
          var goodId = '';
          profile.humanIds.reverse();
          for (var i = 0; i < profile.humanIds.length; ++i) {
            var id = profile.humanIds[i];
            if (id.active) {
              goodId = id.idString;
              break;
            }
          }

          return {humanId: goodId, zombieId: params.readZombieId};
        }
      }).catch((err) => {
        this.get('errorHandler').handleError(err, 'Unable to retrieve your id');

        if (params.readHumanId || params.readZombieId) {
          this.get('toast').warning("Sorry, you can't use QR codes unless you are logged into the website!");
        }

        return {humanId: '', zombieId: ''};
      });
    }
    else {
      return {notLoggedIn: true};
    }
  },

  actions: {
    didTransition() {
      if (this.get('locationId')) {
        navigator.geolocation.clearWatch(this.get('locationId'));
      }

      this.set('locationId', navigator.geolocation.watchPosition((loc) => {
        Ember.$('#location-status').html("SENDING (accuracy: " + loc.coords.accuracy + " meters)");
        this.set('useLocation', true);
        this.set('latitude', loc.coords.latitude);
        this.set('longitude', loc.coords.longitude);
      }, (err) => {
        console.log(err);
        Ember.$('#location-status').innerHTML = "NOT SENDING";
        this.set('useLocation', false);
      }, {
        enableHighAccuracy: true,
        timeout: Infinity,
        maximumAge: 0
      }));
    },

    willTransition() {
      if (this.get('locationId')) {
        navigator.geolocation.clearWatch(this.get('locationId'));
        this.set('locationId', null);
      }

      this.set('useLocation', false);
    },

    submitInfection() {
      var humanId = Ember.$("#humanId").val().trim();
      var zombieId = Ember.$("#zombieId").val().trim();

      var hasError = false;
      if (humanId === '') {
        this.get('toast').error("You didn't enter an id for the human!");
        hasError = true;
      }
      else if (humanId.length !== 8) {
        this.get('toast').error("The human id doesn't look valid.");
        hasError = true;
      }

      if (zombieId === '') {
        this.get('toast').error("You didn't enter an id for the zombie!");
        hasError = true;
      }
      else if (zombieId.length !== 8) {
        this.get('toast').error("The zombie id doesn't look valid.");
        hasError = true;
      }

      if (hasError) {
        return;
      }

      Ember.$("#infectButton").hide();

      this.get('ajax').post('/game/infect', {
        data: {
          human: humanId,
          zombie: zombieId,
          location: this.get('useLocation'),
          latitude: this.get('latitude'),
          longitude: this.get('longitude'),
          apikey: this.get('user').getApiKey()
        }
      }).then((result) => {
        this.get('toast').success('' + result.human.name + ' has joined the horde, courtesy of ' + result.zombie.name + '');
        Ember.$('#humanId').val('');
        Ember.$('#infectButton').show();
      }).catch((err) => {
        if (err.errors[0] && err.errors[0].detail && err.errors[0].detail.problems) {
          console.log(err);
          err.errors[0].detail.problems.forEach((message) => {
            this.get('toast').error(message);
          });
        }
        else {
          this.get('errorHandler').handleError(err, 'There was a problem processing the infection.');
        }

        Ember.$('#infectButton').show();
      });
    }
  }
});
<commit_msg>check for geolocation support<commit_after>import Ember from "ember";

export default Ember.Route.extend({
  ajax: Ember.inject.service(),
  user: Ember.inject.service(),
  errorHandler: Ember.inject.service(),
  toast: Ember.inject.service(),

  useLocation: false,
  locationId: null,
  latitude: null,
  longitude: null,
  accuracy: null,

  queryParams: {
    readHumanId: {
      refreshModel: true,
      as: 'human'
    },

    readZombieId: {
      refreshModel: true,
      as: 'zombie'
    }
  },

  model(params) {
    if (this.get('user').isLoggedIn()) {
      return this.get('user').getUserProfile().then((result) => {
        var profile = result.profile;
        if (profile.team === 'zombie') {
          return {humanId: params.readHumanId, zombieId: profile.zombieId};
        }
        else {
          var goodId = '';
          profile.humanIds.reverse();
          for (var i = 0; i < profile.humanIds.length; ++i) {
            var id = profile.humanIds[i];
            if (id.active) {
              goodId = id.idString;
              break;
            }
          }

          return {humanId: goodId, zombieId: params.readZombieId};
        }
      }).catch((err) => {
        this.get('errorHandler').handleError(err, 'Unable to retrieve your id');

        if (params.readHumanId || params.readZombieId) {
          this.get('toast').warning("Sorry, you can't use QR codes unless you are logged into the website!");
        }

        return {humanId: '', zombieId: ''};
      });
    }
    else {
      return {notLoggedIn: true};
    }
  },

  actions: {
    didTransition() {
      if (this.get('locationId')) {
        if (navigator && navigator.geolocation) {
          navigator.geolocation.clearWatch(this.get('locationId'));
        }
      }

      if (navigator && navigator.geolocation) {
        this.set('locationId', navigator.geolocation.watchPosition((loc) => {
          Ember.$('#location-status').html("SENDING (accuracy: " + loc.coords.accuracy + " meters)");
          this.set('useLocation', true);
          this.set('latitude', loc.coords.latitude);
          this.set('longitude', loc.coords.longitude);
        }, (err) => {
          console.log(err);
          Ember.$('#location-status').innerHTML = "NOT SENDING";
          this.set('useLocation', false);
        }, {
          enableHighAccuracy: true,
          timeout: Infinity,
          maximumAge: 0
        }));
      }
    },

    willTransition() {
      if (this.get('locationId')) {
        if (navigator && navigator.geolocation) {
          navigator.geolocation.clearWatch(this.get('locationId'));
        }
        this.set('locationId', null);
      }

      this.set('useLocation', false);
    },

    submitInfection() {
      var humanId = Ember.$("#humanId").val().trim();
      var zombieId = Ember.$("#zombieId").val().trim();

      var hasError = false;
      if (humanId === '') {
        this.get('toast').error("You didn't enter an id for the human!");
        hasError = true;
      }
      else if (humanId.length !== 8) {
        this.get('toast').error("The human id doesn't look valid.");
        hasError = true;
      }

      if (zombieId === '') {
        this.get('toast').error("You didn't enter an id for the zombie!");
        hasError = true;
      }
      else if (zombieId.length !== 8) {
        this.get('toast').error("The zombie id doesn't look valid.");
        hasError = true;
      }

      if (hasError) {
        return;
      }

      Ember.$("#infectButton").hide();

      this.get('ajax').post('/game/infect', {
        data: {
          human: humanId,
          zombie: zombieId,
          location: this.get('useLocation'),
          latitude: this.get('latitude'),
          longitude: this.get('longitude'),
          apikey: this.get('user').getApiKey()
        }
      }).then((result) => {
        this.get('toast').success('' + result.human.name + ' has joined the horde, courtesy of ' + result.zombie.name + '');
        Ember.$('#humanId').val('');
        Ember.$('#infectButton').show();
      }).catch((err) => {
        if (err.errors[0] && err.errors[0].detail && err.errors[0].detail.problems) {
          console.log(err);
          err.errors[0].detail.problems.forEach((message) => {
            this.get('toast').error(message);
          });
        }
        else {
          this.get('errorHandler').handleError(err, 'There was a problem processing the infection.');
        }

        Ember.$('#infectButton').show();
      });
    }
  }
});
<|endoftext|>


<commit_before>import { List, Map, OrderedSet, Record, Set } from 'immutable'
import cuid from 'cuid'
import * as SortedList from '../../shared/sorted-list'
import {
  CHAT_CHANNEL_NONVISIBLE,
  CHAT_INIT_CHANNEL,
  CHAT_LOAD_CHANNEL_HISTORY_BEGIN,
  CHAT_LOAD_CHANNEL_HISTORY,
  CHAT_LOAD_USER_LIST_BEGIN,
  CHAT_LOAD_USER_LIST,
  CHAT_UPDATE_MESSAGE,
  CHAT_UPDATE_USER_ACTIVE,
  CHAT_UPDATE_USER_IDLE,
  CHAT_UPDATE_USER_OFFLINE,
} from '../actions'

// TODO(tec27): need to track what channels are *active* as well, so we can discard single message
// updates for a channel

// How many messages should be kept for inactive channels
const INACTIVE_CHANNEL_MAX_HISTORY = 250

const sortUsers = (a, b) => a.localeCompare(b)

// Create partial evaluations all the SortedList functions with our sort function pre-applied
const SortedUsers =
    Object.keys(SortedList)
      .map(fnName => [fnName, (...args) => SortedList[fnName](sortUsers, ...args)])
      .reduce((prev, cur) => {
        prev[cur[0]] = cur[1]
        return prev
      }, {})

export const Users = new Record({
  active: new List(),
  idle: new List(),
  offline: new List(),
})

export const Channel = new Record({
  name: null,
  messages: new List(),
  users: new Users(),

  hasLoadedHistory: false,
  loadingHistory: false,
  hasHistory: true,

  hasLoadedUserList: false,
  loadingUserList: false,
})

export const ChatState = new Record({
  channels: new OrderedSet(),
  byName: new Map(),
})

// id, type, and time need to be present for ALL message types
export const ChatMessage = new Record({
  id: null,
  type: 'message',
  time: 0,
  from: null,
  text: null,
})
export const UserOnlineMessage = new Record({
  id: null,
  type: 'userOnline',
  time: 0,
  user: null,
})
export const UserOfflineMessage = new Record({
  id: null,
  type: 'userOffline',
  time: 0,
  user: null,
})

function updateUserState(user, addTo, removeFirst, removeSecond) {
  const addToUpdated = SortedUsers.insert(addTo, user)

  const firstIndex = SortedUsers.findIndex(removeFirst, user)
  const removeFirstUpdated = firstIndex > -1 ? removeFirst.remove(firstIndex) : removeFirst

  const secondIndex = SortedUsers.findIndex(removeSecond, user)
  const removeSecondUpdated = secondIndex !== -1 ? removeSecond.remove(secondIndex) : removeSecond

  return [ addToUpdated, removeFirstUpdated, removeSecondUpdated ]
}

const handlers = {
  [CHAT_INIT_CHANNEL](state, action) {
    const { channel, activeUsers } = action.payload
    const sortedActiveUsers = SortedUsers.create(activeUsers)
    const record = new Channel({
      name: channel,
      users: new Users({
        active: sortedActiveUsers,
      })
    })
    return (state.update('channels', c => c.add(channel))
      .setIn(['byName', channel], record))
  },

  [CHAT_UPDATE_MESSAGE](state, action) {
    const { id, channel, time, user, message } = action.payload
    return state.updateIn(['byName', channel, 'messages'], m => {
      return m.push(new ChatMessage({
        id,
        time,
        from: user,
        text: message,
      }))
    })
  },

  [CHAT_UPDATE_USER_ACTIVE](state, action) {
    const { channel, user } = action.payload
    let wasIdle = false
    let updated = state.updateIn(['byName', channel, 'users'], users => {
      const [ active, idle, offline ] =
          updateUserState(user, users.active, users.idle, users.offline)
      wasIdle = idle !== users.idle

      return users.set('active', active).set('idle', idle).set('offline', offline)
    })

    if (!wasIdle) {
      updated = updated.updateIn(['byName', channel, 'messages'], m => {
        return m.push(new UserOnlineMessage({
          id: cuid(),
          time: Date.now(),
          user,
        }))
      })
    }

    return updated
  },

  [CHAT_UPDATE_USER_IDLE](state, action) {
    const { channel, user } = action.payload
    return state.updateIn(['byName', channel, 'users'], users => {
      const [ idle, active, offline ] =
          updateUserState(user, users.idle, users.active, users.offline)

      return users.set('active', active).set('idle', idle).set('offline', offline)
    })
  },

  [CHAT_UPDATE_USER_OFFLINE](state, action) {
    const { channel, user } = action.payload
    return state.updateIn(['byName', channel, 'users'], users => {
      const [ offline, active, idle ] =
          updateUserState(user, users.offline, users.active, users.idle)

      return users.set('active', active).set('idle', idle).set('offline', offline)
    }).updateIn(['byName', channel, 'messages'], m => {
      return m.push(new UserOfflineMessage({
        id: cuid(),
        time: Date.now(),
        user,
      }))
    })
  },

  [CHAT_LOAD_CHANNEL_HISTORY_BEGIN](state, action) {
    const { channel } = action.payload
    return (state.setIn(['byName', channel, 'hasLoadedHistory'], true)
      .setIn(['byName', channel, 'loadingHistory'], true))
  },

  [CHAT_LOAD_CHANNEL_HISTORY](state, action) {
    const { channel } = action.meta
    const newMessages = action.payload
    const updated = state.setIn(['byName', channel, 'loadingHistory'], false)
    if (!newMessages.length) {
      return updated.setIn(['byName', channel, 'hasHistory'], false)
    }

    // TODO(tec27): remove dupes when doing the message merge
    return updated.updateIn(['byName', channel, 'messages'], messages => {
      return new List(newMessages.map(msg => new ChatMessage({
        id: msg.id,
        time: msg.sent,
        from: msg.user,
        text: msg.data.text,
      }))).concat(messages)
    })
  },

  [CHAT_LOAD_USER_LIST_BEGIN](state, action) {
    const { channel } = action.payload
    return (state.setIn(['byName', channel, 'hasLoadedUserList'], true)
      .setIn(['byName', channel, 'loadingUserList'], true))
  },

  [CHAT_LOAD_USER_LIST](state, action) {
    const { channel } = action.meta
    const userList = action.payload
    return (state.setIn(['byName', channel, 'loadingUserList'], false)
      .updateIn(['byName', channel, 'users'], users => {
        const offline =
            SortedUsers.create(new Set(userList).subtract(users.active).subtract(users.idle))
        return users.set('offline', offline)
      }))
  },

  [CHAT_CHANNEL_NONVISIBLE](state, action) {
    const { channel } = action.payload
    if (state.byName.get(channel).messages.length < INACTIVE_CHANNEL_MAX_HISTORY) {
      return state
    }

    return state.updateIn(['byName', channel], c => {
      return (c.set('messages', c.messages.slice(-INACTIVE_CHANNEL_MAX_HISTORY))
        .set('hasHistory', true))
    })
  }
}

export default function(state = new ChatState(), action) {
  return handlers.hasOwnProperty(action.type) ? handlers[action.type](state, action) : state
}
<commit_msg>Fix message count check in chat-reducer to use size (length isn't a thing on Immutables).<commit_after>import { List, Map, OrderedSet, Record, Set } from 'immutable'
import cuid from 'cuid'
import * as SortedList from '../../shared/sorted-list'
import {
  CHAT_CHANNEL_NONVISIBLE,
  CHAT_INIT_CHANNEL,
  CHAT_LOAD_CHANNEL_HISTORY_BEGIN,
  CHAT_LOAD_CHANNEL_HISTORY,
  CHAT_LOAD_USER_LIST_BEGIN,
  CHAT_LOAD_USER_LIST,
  CHAT_UPDATE_MESSAGE,
  CHAT_UPDATE_USER_ACTIVE,
  CHAT_UPDATE_USER_IDLE,
  CHAT_UPDATE_USER_OFFLINE,
} from '../actions'

// TODO(tec27): need to track what channels are *active* as well, so we can discard single message
// updates for a channel

// How many messages should be kept for inactive channels
const INACTIVE_CHANNEL_MAX_HISTORY = 250

const sortUsers = (a, b) => a.localeCompare(b)

// Create partial evaluations all the SortedList functions with our sort function pre-applied
const SortedUsers =
    Object.keys(SortedList)
      .map(fnName => [fnName, (...args) => SortedList[fnName](sortUsers, ...args)])
      .reduce((prev, cur) => {
        prev[cur[0]] = cur[1]
        return prev
      }, {})

export const Users = new Record({
  active: new List(),
  idle: new List(),
  offline: new List(),
})

export const Channel = new Record({
  name: null,
  messages: new List(),
  users: new Users(),

  hasLoadedHistory: false,
  loadingHistory: false,
  hasHistory: true,

  hasLoadedUserList: false,
  loadingUserList: false,
})

export const ChatState = new Record({
  channels: new OrderedSet(),
  byName: new Map(),
})

// id, type, and time need to be present for ALL message types
export const ChatMessage = new Record({
  id: null,
  type: 'message',
  time: 0,
  from: null,
  text: null,
})
export const UserOnlineMessage = new Record({
  id: null,
  type: 'userOnline',
  time: 0,
  user: null,
})
export const UserOfflineMessage = new Record({
  id: null,
  type: 'userOffline',
  time: 0,
  user: null,
})

function updateUserState(user, addTo, removeFirst, removeSecond) {
  const addToUpdated = SortedUsers.insert(addTo, user)

  const firstIndex = SortedUsers.findIndex(removeFirst, user)
  const removeFirstUpdated = firstIndex > -1 ? removeFirst.remove(firstIndex) : removeFirst

  const secondIndex = SortedUsers.findIndex(removeSecond, user)
  const removeSecondUpdated = secondIndex !== -1 ? removeSecond.remove(secondIndex) : removeSecond

  return [ addToUpdated, removeFirstUpdated, removeSecondUpdated ]
}

const handlers = {
  [CHAT_INIT_CHANNEL](state, action) {
    const { channel, activeUsers } = action.payload
    const sortedActiveUsers = SortedUsers.create(activeUsers)
    const record = new Channel({
      name: channel,
      users: new Users({
        active: sortedActiveUsers,
      })
    })
    return (state.update('channels', c => c.add(channel))
      .setIn(['byName', channel], record))
  },

  [CHAT_UPDATE_MESSAGE](state, action) {
    const { id, channel, time, user, message } = action.payload
    return state.updateIn(['byName', channel, 'messages'], m => {
      return m.push(new ChatMessage({
        id,
        time,
        from: user,
        text: message,
      }))
    })
  },

  [CHAT_UPDATE_USER_ACTIVE](state, action) {
    const { channel, user } = action.payload
    let wasIdle = false
    let updated = state.updateIn(['byName', channel, 'users'], users => {
      const [ active, idle, offline ] =
          updateUserState(user, users.active, users.idle, users.offline)
      wasIdle = idle !== users.idle

      return users.set('active', active).set('idle', idle).set('offline', offline)
    })

    if (!wasIdle) {
      updated = updated.updateIn(['byName', channel, 'messages'], m => {
        return m.push(new UserOnlineMessage({
          id: cuid(),
          time: Date.now(),
          user,
        }))
      })
    }

    return updated
  },

  [CHAT_UPDATE_USER_IDLE](state, action) {
    const { channel, user } = action.payload
    return state.updateIn(['byName', channel, 'users'], users => {
      const [ idle, active, offline ] =
          updateUserState(user, users.idle, users.active, users.offline)

      return users.set('active', active).set('idle', idle).set('offline', offline)
    })
  },

  [CHAT_UPDATE_USER_OFFLINE](state, action) {
    const { channel, user } = action.payload
    return state.updateIn(['byName', channel, 'users'], users => {
      const [ offline, active, idle ] =
          updateUserState(user, users.offline, users.active, users.idle)

      return users.set('active', active).set('idle', idle).set('offline', offline)
    }).updateIn(['byName', channel, 'messages'], m => {
      return m.push(new UserOfflineMessage({
        id: cuid(),
        time: Date.now(),
        user,
      }))
    })
  },

  [CHAT_LOAD_CHANNEL_HISTORY_BEGIN](state, action) {
    const { channel } = action.payload
    return (state.setIn(['byName', channel, 'hasLoadedHistory'], true)
      .setIn(['byName', channel, 'loadingHistory'], true))
  },

  [CHAT_LOAD_CHANNEL_HISTORY](state, action) {
    const { channel } = action.meta
    const newMessages = action.payload
    const updated = state.setIn(['byName', channel, 'loadingHistory'], false)
    if (!newMessages.length) {
      return updated.setIn(['byName', channel, 'hasHistory'], false)
    }

    // TODO(tec27): remove dupes when doing the message merge
    return updated.updateIn(['byName', channel, 'messages'], messages => {
      return new List(newMessages.map(msg => new ChatMessage({
        id: msg.id,
        time: msg.sent,
        from: msg.user,
        text: msg.data.text,
      }))).concat(messages)
    })
  },

  [CHAT_LOAD_USER_LIST_BEGIN](state, action) {
    const { channel } = action.payload
    return (state.setIn(['byName', channel, 'hasLoadedUserList'], true)
      .setIn(['byName', channel, 'loadingUserList'], true))
  },

  [CHAT_LOAD_USER_LIST](state, action) {
    const { channel } = action.meta
    const userList = action.payload
    return (state.setIn(['byName', channel, 'loadingUserList'], false)
      .updateIn(['byName', channel, 'users'], users => {
        const offline =
            SortedUsers.create(new Set(userList).subtract(users.active).subtract(users.idle))
        return users.set('offline', offline)
      }))
  },

  [CHAT_CHANNEL_NONVISIBLE](state, action) {
    const { channel } = action.payload
    if (state.byName.get(channel).messages.size < INACTIVE_CHANNEL_MAX_HISTORY) {
      return state
    }

    return state.updateIn(['byName', channel], c => {
      return (c.set('messages', c.messages.slice(-INACTIVE_CHANNEL_MAX_HISTORY))
        .set('hasHistory', true))
    })
  }
}

export default function(state = new ChatState(), action) {
  return handlers.hasOwnProperty(action.type) ? handlers[action.type](state, action) : state
}
<|endoftext|>