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

/**
 * Abstract base class for model in Graphing Quadratics.
 *
 * @author Andrea Lin
 * @author Chris Malley (PixelZoom, Inc.)
 */

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import Property from '../../../../axon/js/Property.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import Graph from '../../../../graphing-lines/js/common/model/Graph.js';
import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js';
import NullableIO from '../../../../tandem/js/types/NullableIO.js';
import graphingQuadratics from '../../graphingQuadratics.js';
import GQColors from '../GQColors.js';
import GQConstants from '../GQConstants.js';
import PointTool from './PointTool.js';
import Quadratic from './Quadratic.js';

// constants
const GRAPH_VIEW_ORIGIN = new Vector2( 345, 330 ); // position of the graph's origin, in view coordinates
const GRAPH_VIEW_WIDTH = 530; // width of the graph, in view coordinates

class GQModel {

  /**
   * @param {Property.<Quadratic>} quadraticProperty - the interactive quadratic
   * @param {Tandem} tandem
   * @abstract
   */
  constructor( quadraticProperty, tandem ) {

    // @public {Property.<Quadratic>} the interactive quadratic
    this.quadraticProperty = quadraticProperty;

    // @public (read-only) graph
    this.graph = new Graph( GQConstants.X_AXIS_RANGE, GQConstants.Y_AXIS_RANGE );

    // @public {Property.<Quadratic|null>} the saved quadratic, null if nothing is saved
    this.savedQuadraticProperty = new Property( null, {
      valueType: [ Quadratic, null ],
      tandem: tandem.createTandem( 'savedQuadraticProperty' ),
      phetioDocumentation: 'the saved quadratic, null if there is no saved quadratic',
      phetioType: Property.PropertyIO( NullableIO( Quadratic.QuadraticIO ) )
    } );

    // scale from model to view
    const modelViewTransformScale = GRAPH_VIEW_WIDTH / this.graph.xRange.getLength();

    // @public (read-only) model-view transform, created in the model because it's dependent on graph axes ranges.
    this.modelViewTransform = ModelViewTransform2.createOffsetXYScaleMapping(
      GRAPH_VIEW_ORIGIN,
      modelViewTransformScale,
      -modelViewTransformScale // y is inverted (+y is up in the model, +y is down in the view)
    );

    // Options for {Property.<Quadratic[]>}
    const optionsPropertyQuadraticArray = {
      isValidValue: array => Array.isArray( array ) &&
                             _.every( array, function( value ) { return value instanceof Quadratic; } )
    };

    // @public {Property.<Quadratic[]>} optional quadratic terms to be displayed,
    // in the order that they will be considered by point tools (foreground to background).
    // ObservableArrayDef is not used here because we need to change the entire array contents atomically.
    this.quadraticTermsProperty = new Property( [], optionsPropertyQuadraticArray );

    // {DerivedProperty.<Quadratic[]>} Quadratics that are visible to the point tools,
    // in the order that they will be considered by point tools (foreground to background).
    // ObservableArrayDef is not used here because we need to change the entire array contents atomically.
    const pointToolQuadraticsProperty = new DerivedProperty(
      [ this.quadraticProperty, this.quadraticTermsProperty, this.savedQuadraticProperty ],
      ( quadratic, quadraticTerms, savedQuadratic ) => {
        // order is important! compact to remove nulls
        return _.compact( [ quadratic, ...quadraticTerms, savedQuadratic ] );
      }, optionsPropertyQuadraticArray );

    // @public (read-only)
    this.leftPointTool = new PointTool( pointToolQuadraticsProperty, this.graph, {
      probeSide: 'right', // probe is attached to the right side
      position: new Vector2( -2, this.graph.yRange.min - 2 ), // below the graph
      dragBounds: new Bounds2(
        this.graph.xRange.min - 1, this.graph.yRange.min - 3,
        this.graph.xRange.max + 1, this.graph.yRange.max + 1 ),
      tandem: tandem.createTandem( 'leftPointTool' ),
      phetioDocumentation: 'The point tool that is initially on the left. It is typically grabbed from the left, and its probe is on the right.'
    } );

    // @public (read-only)
    this.rightPointTool = new PointTool( pointToolQuadraticsProperty, this.graph, {
      probeSide: 'left', // probe is attached to the left side
      position: new Vector2( 2, this.graph.yRange.min - 2 ), // below the graph
      dragBounds: new Bounds2(
        this.graph.xRange.min - 1, this.graph.yRange.min - 3,
        this.graph.xRange.max + 1, this.graph.yRange.max + 1 ),
      tandem: tandem.createTandem( 'rightPointTool' ),
      phetioDocumentation: 'The point tool that is initially on the right. It is typically grabbed from the right, and its probe is on the left.'
    } );
  }

  // @public
  reset() {
    this.savedQuadraticProperty.reset();
    this.leftPointTool.reset();
    this.rightPointTool.reset();
  }

  /**
   * Saves the interactive quadratic.
   * @public
   */
  saveQuadratic() {
    this.savedQuadraticProperty.value = this.quadraticProperty.value.withColor( GQColors.SAVED_CURVE );
  }

  /**
   * Erases the saved quadratic.
   * @public
   */
  eraseQuadratic() {
    this.savedQuadraticProperty.value = null;
  }
}

graphingQuadratics.register( 'GQModel', GQModel );
export default GQModel;<commit_msg>ESLint: Autofix prefer-arrow-callback rule, see https://github.com/phetsims/chipper/issues/814<commit_after>// Copyright 2014-2020, University of Colorado Boulder

/**
 * Abstract base class for model in Graphing Quadratics.
 *
 * @author Andrea Lin
 * @author Chris Malley (PixelZoom, Inc.)
 */

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import Property from '../../../../axon/js/Property.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import Graph from '../../../../graphing-lines/js/common/model/Graph.js';
import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js';
import NullableIO from '../../../../tandem/js/types/NullableIO.js';
import graphingQuadratics from '../../graphingQuadratics.js';
import GQColors from '../GQColors.js';
import GQConstants from '../GQConstants.js';
import PointTool from './PointTool.js';
import Quadratic from './Quadratic.js';

// constants
const GRAPH_VIEW_ORIGIN = new Vector2( 345, 330 ); // position of the graph's origin, in view coordinates
const GRAPH_VIEW_WIDTH = 530; // width of the graph, in view coordinates

class GQModel {

  /**
   * @param {Property.<Quadratic>} quadraticProperty - the interactive quadratic
   * @param {Tandem} tandem
   * @abstract
   */
  constructor( quadraticProperty, tandem ) {

    // @public {Property.<Quadratic>} the interactive quadratic
    this.quadraticProperty = quadraticProperty;

    // @public (read-only) graph
    this.graph = new Graph( GQConstants.X_AXIS_RANGE, GQConstants.Y_AXIS_RANGE );

    // @public {Property.<Quadratic|null>} the saved quadratic, null if nothing is saved
    this.savedQuadraticProperty = new Property( null, {
      valueType: [ Quadratic, null ],
      tandem: tandem.createTandem( 'savedQuadraticProperty' ),
      phetioDocumentation: 'the saved quadratic, null if there is no saved quadratic',
      phetioType: Property.PropertyIO( NullableIO( Quadratic.QuadraticIO ) )
    } );

    // scale from model to view
    const modelViewTransformScale = GRAPH_VIEW_WIDTH / this.graph.xRange.getLength();

    // @public (read-only) model-view transform, created in the model because it's dependent on graph axes ranges.
    this.modelViewTransform = ModelViewTransform2.createOffsetXYScaleMapping(
      GRAPH_VIEW_ORIGIN,
      modelViewTransformScale,
      -modelViewTransformScale // y is inverted (+y is up in the model, +y is down in the view)
    );

    // Options for {Property.<Quadratic[]>}
    const optionsPropertyQuadraticArray = {
      isValidValue: array => Array.isArray( array ) &&
                             _.every( array, value => value instanceof Quadratic )
    };

    // @public {Property.<Quadratic[]>} optional quadratic terms to be displayed,
    // in the order that they will be considered by point tools (foreground to background).
    // ObservableArrayDef is not used here because we need to change the entire array contents atomically.
    this.quadraticTermsProperty = new Property( [], optionsPropertyQuadraticArray );

    // {DerivedProperty.<Quadratic[]>} Quadratics that are visible to the point tools,
    // in the order that they will be considered by point tools (foreground to background).
    // ObservableArrayDef is not used here because we need to change the entire array contents atomically.
    const pointToolQuadraticsProperty = new DerivedProperty(
      [ this.quadraticProperty, this.quadraticTermsProperty, this.savedQuadraticProperty ],
      ( quadratic, quadraticTerms, savedQuadratic ) => {
        // order is important! compact to remove nulls
        return _.compact( [ quadratic, ...quadraticTerms, savedQuadratic ] );
      }, optionsPropertyQuadraticArray );

    // @public (read-only)
    this.leftPointTool = new PointTool( pointToolQuadraticsProperty, this.graph, {
      probeSide: 'right', // probe is attached to the right side
      position: new Vector2( -2, this.graph.yRange.min - 2 ), // below the graph
      dragBounds: new Bounds2(
        this.graph.xRange.min - 1, this.graph.yRange.min - 3,
        this.graph.xRange.max + 1, this.graph.yRange.max + 1 ),
      tandem: tandem.createTandem( 'leftPointTool' ),
      phetioDocumentation: 'The point tool that is initially on the left. It is typically grabbed from the left, and its probe is on the right.'
    } );

    // @public (read-only)
    this.rightPointTool = new PointTool( pointToolQuadraticsProperty, this.graph, {
      probeSide: 'left', // probe is attached to the left side
      position: new Vector2( 2, this.graph.yRange.min - 2 ), // below the graph
      dragBounds: new Bounds2(
        this.graph.xRange.min - 1, this.graph.yRange.min - 3,
        this.graph.xRange.max + 1, this.graph.yRange.max + 1 ),
      tandem: tandem.createTandem( 'rightPointTool' ),
      phetioDocumentation: 'The point tool that is initially on the right. It is typically grabbed from the right, and its probe is on the left.'
    } );
  }

  // @public
  reset() {
    this.savedQuadraticProperty.reset();
    this.leftPointTool.reset();
    this.rightPointTool.reset();
  }

  /**
   * Saves the interactive quadratic.
   * @public
   */
  saveQuadratic() {
    this.savedQuadraticProperty.value = this.quadraticProperty.value.withColor( GQColors.SAVED_CURVE );
  }

  /**
   * Erases the saved quadratic.
   * @public
   */
  eraseQuadratic() {
    this.savedQuadraticProperty.value = null;
  }
}

graphingQuadratics.register( 'GQModel', GQModel );
export default GQModel;<|endoftext|>


<commit_before>subtest('width', function () {
    is(width('あいうえお'), 10);
    is(width('...'), 3);
    is(width('Shinjuku'), 8);
    is(width("\u2026"), 2); // ambiguous char should be full width
});
subtest('width/surrogate pair', function () {
    is(width(String.fromCharCode(0xD800, 0xDC00)), 1); // U+10000 LINEAR B SYLLABLE B008 A (first non-BMP code point)    ( half width )
    is(width(String.fromCharCode(0xD840, 0xDC0B)), 2);
    is(width(String.fromCharCode(0xD869, 0xDEB2)), 2);
    is(width(String.fromCharCode(0xD840, 0xDC0B) + String.fromCharCode(0xD869, 0xDEB2)), 4);
});

subtest('truncate', function () {
    is(truncate('無印良品 アキバ・トリム', 20, '...'), '無印良品 アキバ・...');
    is(truncate('Shinjuku', 15, '…'), 'Shinjuku');
    is(truncate('あいうえお', 12, '...'), 'あいうえお');
    is(truncate('あいうえお', 11, '...'), 'あいうえお');
    is(truncate('あいうえお', 10, '...'), 'あいうえお');
    is(truncate('あいうえお', 9,  '...'), 'あいう...');
    is(truncate('あいうえお', 8,  '...'), 'あい...');
    is(truncate('あいうえお', 7,  '...'), 'あい...');
    is(truncate('あいうえお', 6,  '...'), 'あ...');
    is(truncate('あいうえお', 5,  '...'), 'あ...');
});

<commit_msg>more test cases<commit_after>subtest('width', function () {
    is(width('あいうえお'), 10);
    is(width('...'), 3);
    is(width('Shinjuku'), 8);
    is(width('…'), 2);
    is(width("\u2026"), 2); // ambiguous char should be full width
});
subtest('width/surrogate pair', function () {
    is(width(String.fromCharCode(0xD800, 0xDC00)), 1); // U+10000 LINEAR B SYLLABLE B008 A (first non-BMP code point)    ( half width )
    is(width(String.fromCharCode(0xD840, 0xDC0B)), 2);
    is(width(String.fromCharCode(0xD869, 0xDEB2)), 2);
    is(width(String.fromCharCode(0xD840, 0xDC0B) + String.fromCharCode(0xD869, 0xDEB2)), 4);
});

subtest('truncate', function () {
    is(truncate('DOUTOR 新宿アイランド店', 15, '...'), 'DOUTOR 新宿...');
    is(truncate('無印良品 アキバ・トリム', 20, '...'), '無印良品 アキバ・...');
    is(truncate('VILLAGE VANGUARD 渋谷宇田川', 15, '...'), 'VILLAGE VANG...');
    is(truncate('VILLAGE VANGUARD 渋谷宇田川', 15, '...').length, 15);
    is(truncate('Shinjuku', 15, '…'), 'Shinjuku');
    is(truncate('あいうえお', 12, '...'), 'あいうえお');
    is(truncate('あいうえお', 11, '...'), 'あいうえお');
    is(truncate('あいうえお', 10, '...'), 'あいうえお');
    is(truncate('あいうえお', 9,  '...'), 'あいう...');
    is(truncate('あいうえお', 8,  '...'), 'あい...');
    is(truncate('あいうえお', 7,  '...'), 'あい...');
    is(truncate('あいうえお', 6,  '...'), 'あ...');
    is(truncate('あいうえお', 5,  '...'), 'あ...');
});

<|endoftext|>


<commit_before>/*
 * Base functions for the registration module
 */

/* jshint browser: true */
/* jshint -W097 */
/* global confirmbox, showSpinner, showSidebar */

'use strict';

var sidebarMainDiv = 'main_content';

function refreshBadgeData(badge) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      location.reload();
    }
    else if (this.status == 404) {
      window.alert('404!');
    }
    else if (this.status == 409) {
      window.alert('409!');
    }
  };
  showSpinner();
  xhttp.open('POST', 'index.php?Function=registration', true);
  xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  xhttp.send('refreshData=' + badge);
}

var _badgeData = null;

function doPrintBadge() {
  confirmbox.close();
  window.location = 'index.php?Function=registration&printBadge=' + _badgeData;
}

function printBadge(data) {
  var input = JSON.parse(atob(data));
  _badgeData = data;
  var msg;
  if (input['Print Count'] > 0) {
    msg =  'Reprint badge for \'';
  } else {
    msg =  'Print badge for \'';
  }
  confirmbox.start('Confirm Print Badge',
      msg + input['Badge Name'] + '\' ?',
      doPrintBadge);
}

function showUpdateBadge(data) {
  var input = JSON.parse(atob(data));
  _badgeData = data;
  showSidebar('update_badge_div');
  document.getElementById('badge_name').value = input['Badge Name'];
}

function updateBadge() {

  var data = JSON.parse(atob(_badgeData));
  data['Badge Name'] = document.getElementById('badge_name').value;
  var param = btoa(JSON.stringify(data));

  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      location.reload();
    }
    else if (this.status == 404) {
      window.alert('404!');
    }
    else if (this.status == 409) {
      window.alert('409!');
    }
  };
  showSpinner();
  xhttp.open('POST', 'index.php?Function=registration', true);
  xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  xhttp.send('updateBadge=' + param);
}

function workstationChange() {
  var value = document.getElementById('workstation_id').value;
  var d = new Date();
  var exdays = 5;
  d.setTime(d.getTime() + (exdays*24*60*60*1000));
  var expires = ";expires="+ d.toUTCString();
  document.cookie = "CIAB_REGISTRATIONWORKSTATION=" + value + expires +
                    ";path=/";
  var element = document.getElementById('kiosk_mode');
  var disabled = element.classList.contains('UI-disabled');
  var state = (value === "");
  if (state !== disabled) {
        if (state && !disabled)
          element.classList.add('UI-disabled');
        if (disabled && !state)
          element.classList.remove('UI-disabled');
  }
}
<commit_msg>ESLint modules/registration<commit_after>/*
 * Base functions for the registration module
 */

/* jshint browser: true */
/* jshint -W097 */
/* global confirmbox, showSpinner, showSidebar */
/* exported sidebarMainDiv, refreshBadgeData, printBadge, showUpdateBadge,
            updateBadge, workstationChange */

'use strict';

var sidebarMainDiv = 'main_content';

function refreshBadgeData(badge) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      location.reload();
    }
    else if (this.status == 404) {
      window.alert('404!');
    }
    else if (this.status == 409) {
      window.alert('409!');
    }
  };
  showSpinner();
  xhttp.open('POST', 'index.php?Function=registration', true);
  xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  xhttp.send('refreshData=' + badge);
}

var _badgeData = null;

function doPrintBadge() {
  confirmbox.close();
  window.location = 'index.php?Function=registration&printBadge=' + _badgeData;
}

function printBadge(data) {
  var input = JSON.parse(atob(data));
  _badgeData = data;
  var msg;
  if (input['Print Count'] > 0) {
    msg =  'Reprint badge for \'';
  } else {
    msg =  'Print badge for \'';
  }
  confirmbox.start('Confirm Print Badge',
    msg + input['Badge Name'] + '\' ?',
    doPrintBadge);
}

function showUpdateBadge(data) {
  var input = JSON.parse(atob(data));
  _badgeData = data;
  showSidebar('update_badge_div');
  document.getElementById('badge_name').value = input['Badge Name'];
}

function updateBadge() {

  var data = JSON.parse(atob(_badgeData));
  data['Badge Name'] = document.getElementById('badge_name').value;
  var param = btoa(JSON.stringify(data));

  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      location.reload();
    }
    else if (this.status == 404) {
      window.alert('404!');
    }
    else if (this.status == 409) {
      window.alert('409!');
    }
  };
  showSpinner();
  xhttp.open('POST', 'index.php?Function=registration', true);
  xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  xhttp.send('updateBadge=' + param);
}

function workstationChange() {
  var value = document.getElementById('workstation_id').value;
  var d = new Date();
  var exdays = 5;
  d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
  var expires = ';expires=' + d.toUTCString();
  document.cookie = 'CIAB_REGISTRATIONWORKSTATION=' + value + expires +
                    ';path=/';
  var element = document.getElementById('kiosk_mode');
  var disabled = element.classList.contains('UI-disabled');
  var state = (value === '');
  if (state !== disabled) {
    if (state && !disabled)
    {element.classList.add('UI-disabled');}
    if (disabled && !state)
    {element.classList.remove('UI-disabled');}
  }
}
<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class coinone extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'coinone',
            'name': 'CoinOne',
            'countries': 'KR', // Korea
            'rateLimit': 90,
            'version': 'v2',
            'has': {
                'CORS': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg',
                'api': 'https://api.coinone.co.kr',
                'www': 'https://coinone.co.kr',
                'doc': 'https://doc.coinone.co.kr',
            },
            'requiredCredentials': {
                'apiKey': true,
                'secret': true,
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook/',
                        'trades/',
                        'ticker/',
                    ],
                },
                'private': {
                    'post': [
                        'account/btc_deposit_address/',
                        'account/balance/',
                        'account/daily_balance/',
                        'account/user_info/',
                        'account/virtual_account/',
                        'order/cancel_all/',
                        'order/cancel/',
                        'order/limit_buy/',
                        'order/limit_sell/',
                        'order/complete_orders/',
                        'order/limit_orders/',
                        'order/order_info/',
                        'transaction/auth_number/',
                        'transaction/history/',
                        'transaction/krw/history/',
                        'transaction/btc/',
                        'transaction/coin/',
                    ],
                },
            },
            'markets': {
                'BCH/KRW': { 'id': 'bch', 'symbol': 'BCH/KRW', 'base': 'BCH', 'quote': 'KRW' },
                'BTC/KRW': { 'id': 'btc', 'symbol': 'BTC/KRW', 'base': 'BTC', 'quote': 'KRW' },
                'BTG/KRW': { 'id': 'btg', 'symbol': 'BTG/KRW', 'base': 'BTG', 'quote': 'KRW' },
                'ETC/KRW': { 'id': 'etc', 'symbol': 'ETC/KRW', 'base': 'ETC', 'quote': 'KRW' },
                'ETH/KRW': { 'id': 'eth', 'symbol': 'ETH/KRW', 'base': 'ETH', 'quote': 'KRW' },
                'IOT/KRW': { 'id': 'iota', 'symbol': 'IOT/KRW', 'base': 'IOT', 'quote': 'KRW' },
                'LTC/KRW': { 'id': 'ltc', 'symbol': 'LTC/KRW', 'base': 'LTC', 'quote': 'KRW' },
                'QTUM/KRW': { 'id': 'qtum', 'symbol': 'QTUM/KRW', 'base': 'QTUM', 'quote': 'KRW' },
                'XRP/KRW': { 'id': 'xrp', 'symbol': 'XRP/KRW', 'base': 'XRP', 'quote': 'KRW' },
            },
            'fees': {
                'trading': {
                    'tierBased': true,
                    'percentage': true,
                    'taker': 0.001,
                    'maker': 0.001,
                    'tiers': {
                        'taker': [
                            [0, 0.001],
                            [100000000, 0.0009],
                            [1000000000, 0.0008],
                            [5000000000, 0.0007],
                            [10000000000, 0.0006],
                            [20000000000, 0.0005],
                            [30000000000, 0.0004],
                            [40000000000, 0.0003],
                            [50000000000, 0.0002],
                        ],
                        'maker': [
                            [0, 0.001],
                            [100000000, 0.0008],
                            [1000000000, 0.0006],
                            [5000000000, 0.0004],
                            [10000000000, 0.0002],
                            [20000000000, 0],
                            [30000000000, 0],
                            [40000000000, 0],
                            [50000000000, 0],
                        ],
                    },
                },
            },
        });
    }

    async fetchBalance (params = {}) {
        let response = await this.privateGetV2AccountBalance ();
        let result = { 'info': response };
        let ids = Object.keys (this.markets);
        for (let i = 0; i < ids.length; i++) {
            let market = ids[i];
            let id = market['id'];
            let symbol = market['symbol'];
            if (id in response) {
                let balance = response[id];
                let account = {
                    'free': parseFloat (balance['avail']),
                    'used': parseFloat (balance['balance']) - parseFloat (balance['avail']),
                    'total': parseFloat (balance['balance']),
                };
                result[symbol] = account;
            }
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetOrderbook (this.extend ({
            'currency': market['id'],
            'format': 'json',
        }, params));
        return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'qty');
    }

    async fetchTicker (symbol, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetTicker (this.extend ({
            'currency': market['id'],
            'format': 'json',
        }, params));
        return this.parseTicker (response, market);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        let last = this.safeFloat (ticker, 'last');
        let previousClose = this.safeFloat (ticker, 'yesterday_last');
        let change = undefined;
        if (typeof last !== 'undefined' && typeof previousClose !== 'undefined')
            change = previousClose - last;
        let symbol = (typeof market !== 'undefined') ? market['symbol'] : undefined;
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': undefined,
            'bidVolume': undefined,
            'ask': undefined,
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'first'),
            'close': last,
            'last': last,
            'previousClose': previousClose,
            'change': change,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'volume'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    parseTrade (trade, market = undefined) {
        let timestamp = parseInt (trade['timestamp']) * 1000;
        let symbol = (typeof market !== 'undefined') ? market['symbol'] : undefined;
        return {
            'id': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'order': undefined,
            'symbol': symbol,
            'type': undefined,
            'side': undefined,
            'price': this.safeFloat (trade, 'price'),
            'amount': this.safeFloat (trade, 'qty'),
            'fee': undefined,
            'info': trade,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetTrades (this.extend ({
            'currency': market['id'],
            'period': 'hour',
            'format': 'json',
        }, params));
        return this.parseTrades (response['completeOrders'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type !== 'limit')
            throw new ExchangeError (this.id + ' allows limit orders only');
        await this.loadMarkets ();
        let order = {
            'price': price,
            'currency': this.marketId (symbol),
            'qty': amount,
        };
        let method = 'privatePostOrderLimit' + this.capitalize (side);
        let response = await this[method] (this.extend (order, params));
        // todo: return the full order structure
        // return this.parseOrder (response, market);
        let orderId = this.safeString (response, 'orderId');
        return {
            'info': response,
            'id': orderId,
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        return await this.privatePostOrderCancel ({ 'orderID': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let request = this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        let url = this.urls['api'] + '/' + request;
        headers = {};
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let payload = this.stringToBase64 (this.json ({ 'access_token': this.apiKey, 'nonce': nonce }));
            body = payload;
            let signature = this.hmac (payload, this.encode (this.secret.toUpperCase ()), 'sha512', 'hex');
            headers = {
                'content-type': 'application/json',
                'X-COINONE-PAYLOAD': payload,
                'X-COINONE-SIGNATURE': signature,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if ((body[0] === '{') || (body[0] === '[')) {
            let response = JSON.parse (body);
            if ('result' in response) {
                let result = response['result'];
                if (result !== 'success')
                    throw new ExchangeError (this.id + ' ' + body);
            } else {
                throw new ExchangeError (this.id + ' ' + body);
            }
        }
    }
};
<commit_msg>coinone allows limit orders only<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class coinone extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'coinone',
            'name': 'CoinOne',
            'countries': 'KR', // Korea
            'rateLimit': 90,
            'version': 'v2',
            'has': {
                'CORS': false,
                'createMarketOrder': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg',
                'api': 'https://api.coinone.co.kr',
                'www': 'https://coinone.co.kr',
                'doc': 'https://doc.coinone.co.kr',
            },
            'requiredCredentials': {
                'apiKey': true,
                'secret': true,
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook/',
                        'trades/',
                        'ticker/',
                    ],
                },
                'private': {
                    'post': [
                        'account/btc_deposit_address/',
                        'account/balance/',
                        'account/daily_balance/',
                        'account/user_info/',
                        'account/virtual_account/',
                        'order/cancel_all/',
                        'order/cancel/',
                        'order/limit_buy/',
                        'order/limit_sell/',
                        'order/complete_orders/',
                        'order/limit_orders/',
                        'order/order_info/',
                        'transaction/auth_number/',
                        'transaction/history/',
                        'transaction/krw/history/',
                        'transaction/btc/',
                        'transaction/coin/',
                    ],
                },
            },
            'markets': {
                'BCH/KRW': { 'id': 'bch', 'symbol': 'BCH/KRW', 'base': 'BCH', 'quote': 'KRW' },
                'BTC/KRW': { 'id': 'btc', 'symbol': 'BTC/KRW', 'base': 'BTC', 'quote': 'KRW' },
                'BTG/KRW': { 'id': 'btg', 'symbol': 'BTG/KRW', 'base': 'BTG', 'quote': 'KRW' },
                'ETC/KRW': { 'id': 'etc', 'symbol': 'ETC/KRW', 'base': 'ETC', 'quote': 'KRW' },
                'ETH/KRW': { 'id': 'eth', 'symbol': 'ETH/KRW', 'base': 'ETH', 'quote': 'KRW' },
                'IOT/KRW': { 'id': 'iota', 'symbol': 'IOT/KRW', 'base': 'IOT', 'quote': 'KRW' },
                'LTC/KRW': { 'id': 'ltc', 'symbol': 'LTC/KRW', 'base': 'LTC', 'quote': 'KRW' },
                'QTUM/KRW': { 'id': 'qtum', 'symbol': 'QTUM/KRW', 'base': 'QTUM', 'quote': 'KRW' },
                'XRP/KRW': { 'id': 'xrp', 'symbol': 'XRP/KRW', 'base': 'XRP', 'quote': 'KRW' },
            },
            'fees': {
                'trading': {
                    'tierBased': true,
                    'percentage': true,
                    'taker': 0.001,
                    'maker': 0.001,
                    'tiers': {
                        'taker': [
                            [0, 0.001],
                            [100000000, 0.0009],
                            [1000000000, 0.0008],
                            [5000000000, 0.0007],
                            [10000000000, 0.0006],
                            [20000000000, 0.0005],
                            [30000000000, 0.0004],
                            [40000000000, 0.0003],
                            [50000000000, 0.0002],
                        ],
                        'maker': [
                            [0, 0.001],
                            [100000000, 0.0008],
                            [1000000000, 0.0006],
                            [5000000000, 0.0004],
                            [10000000000, 0.0002],
                            [20000000000, 0],
                            [30000000000, 0],
                            [40000000000, 0],
                            [50000000000, 0],
                        ],
                    },
                },
            },
        });
    }

    async fetchBalance (params = {}) {
        let response = await this.privateGetV2AccountBalance ();
        let result = { 'info': response };
        let ids = Object.keys (this.markets);
        for (let i = 0; i < ids.length; i++) {
            let market = ids[i];
            let id = market['id'];
            let symbol = market['symbol'];
            if (id in response) {
                let balance = response[id];
                let account = {
                    'free': parseFloat (balance['avail']),
                    'used': parseFloat (balance['balance']) - parseFloat (balance['avail']),
                    'total': parseFloat (balance['balance']),
                };
                result[symbol] = account;
            }
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetOrderbook (this.extend ({
            'currency': market['id'],
            'format': 'json',
        }, params));
        return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'qty');
    }

    async fetchTicker (symbol, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetTicker (this.extend ({
            'currency': market['id'],
            'format': 'json',
        }, params));
        return this.parseTicker (response, market);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        let last = this.safeFloat (ticker, 'last');
        let previousClose = this.safeFloat (ticker, 'yesterday_last');
        let change = undefined;
        if (typeof last !== 'undefined' && typeof previousClose !== 'undefined')
            change = previousClose - last;
        let symbol = (typeof market !== 'undefined') ? market['symbol'] : undefined;
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': undefined,
            'bidVolume': undefined,
            'ask': undefined,
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'first'),
            'close': last,
            'last': last,
            'previousClose': previousClose,
            'change': change,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'volume'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    parseTrade (trade, market = undefined) {
        let timestamp = parseInt (trade['timestamp']) * 1000;
        let symbol = (typeof market !== 'undefined') ? market['symbol'] : undefined;
        return {
            'id': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'order': undefined,
            'symbol': symbol,
            'type': undefined,
            'side': undefined,
            'price': this.safeFloat (trade, 'price'),
            'amount': this.safeFloat (trade, 'qty'),
            'fee': undefined,
            'info': trade,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetTrades (this.extend ({
            'currency': market['id'],
            'period': 'hour',
            'format': 'json',
        }, params));
        return this.parseTrades (response['completeOrders'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type !== 'limit')
            throw new ExchangeError (this.id + ' allows limit orders only');
        await this.loadMarkets ();
        let order = {
            'price': price,
            'currency': this.marketId (symbol),
            'qty': amount,
        };
        let method = 'privatePostOrder' + this.capitalize (type) + this.capitalize (side);
        let response = await this[method] (this.extend (order, params));
        // todo: return the full order structure
        // return this.parseOrder (response, market);
        let orderId = this.safeString (response, 'orderId');
        return {
            'info': response,
            'id': orderId,
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        return await this.privatePostOrderCancel ({ 'orderID': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let request = this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        let url = this.urls['api'] + '/' + request;
        headers = {};
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let payload = this.stringToBase64 (this.json ({ 'access_token': this.apiKey, 'nonce': nonce }));
            body = payload;
            let signature = this.hmac (payload, this.encode (this.secret.toUpperCase ()), 'sha512', 'hex');
            headers = {
                'content-type': 'application/json',
                'X-COINONE-PAYLOAD': payload,
                'X-COINONE-SIGNATURE': signature,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if ((body[0] === '{') || (body[0] === '[')) {
            let response = JSON.parse (body);
            if ('result' in response) {
                let result = response['result'];
                if (result !== 'success')
                    throw new ExchangeError (this.id + ' ' + body);
            } else {
                throw new ExchangeError (this.id + ' ' + body);
            }
        }
    }
};
<|endoftext|>


<commit_before>// Copyright 2017-2018, University of Colorado Boulder

/**
 * The lattice is a 2D grid with a value in each cell that represents the wave amplitude at that point.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const Bounds2 = require( 'DOT/Bounds2' );
  const Emitter = require( 'AXON/Emitter' );
  const Matrix = require( 'DOT/Matrix' );
  const Util = require( 'DOT/Util' );
  const waveInterference = require( 'WAVE_INTERFERENCE/waveInterference' );

  // constants

  // The wave speed in the coordinate frame of the lattice, see http://www.mtnmath.com/whatth/node47.html. We tried
  // different values, but they do not have the properer emergent behavior.  WAVE_SPEED=1 propagates out as a diamond
  // rather than a circle, and WAVE_SPEED=0.1 is too slow and throws off the frequency of light.
  const WAVE_SPEED = 0.5;
  const WAVE_SPEED_SQUARED = WAVE_SPEED * WAVE_SPEED; // precompute to avoid work in the inner loop
  const NUMBER_OF_MATRICES = 3; // The discretized wave equation algorithm requires current value + 2 history points

  // This is the threshold for the wave value that determines if the light has visited.  If the value is higher,
  // it will track the wavefront of the light more accurately (and hence could be used for more accurate computation of
  // the speed of light), but will generate more artifacts in the initial wave.  If the value is lower, it will generate
  // fewer artifacts in the initial propagation, but will lead the initial wavefront by too far and make it seem like
  // light is faster than it should be measured (based on the propagation of wavefronts).
  const LIGHT_VISIT_THRESHOLD = 0.06;

  class Lattice {

    /**
     * @param {number} width - width of the lattice
     * @param {number} height - height of the lattice
     * @param {number} dampX - number of cells on the left and again on the right to use for damping
     * @param {number} dampY - number of cells on the top and again on the bottom to use for damping
     */
    constructor( width, height, dampX, dampY ) {

      // @public (read-only) {number} - number of cells on the left and again on the right to use for damping
      this.dampX = dampX;

      // @public (read-only) {number} - number of cells on the top and again on the bottom to use for damping
      this.dampY = dampY;

      // @private {Matrix[]} - matrices for current value, previous value and value before previous
      this.matrices = [];
      for ( let i = 0; i < NUMBER_OF_MATRICES; i++ ) {
        this.matrices.push( new Matrix( width, height ) );
      }

      // @private {Matrix} - keeps track of which cells have been visited by the wave
      this.visitedMatrix = new Matrix( width, height );

      // @private {number} - indicates the current matrix. Previous matrix is one higher (with correct modulus)
      this.currentMatrixIndex = 0;

      // @public {Emitter} - sends a notification each time the lattice updates.
      this.changedEmitter = new Emitter();

      // @public (read-only) {number} - width of the lattice (includes damping regions)
      this.width = width;

      // @public (read-only) {number} - height of the lattice (includes damping regions)
      this.height = height;

      // @public {number} - Determines how far we have animated between the "last" and "current" matrices, so that we
      // can use getInterpolatedValue to update the view at 60fps even though the model is running at a slower rate
      this.interpolationRatio = 0;

      // @public (read-only) {Bounds2} - a Bounds2 representing the visible (non-damping) region of the lattice.
      this.visibleBounds = new Bounds2( this.dampX, this.dampY, this.width - this.dampX, this.height - this.dampY );
    }

    /**
     * Gets a Bounds2 representing the full region of the lattice, including damping regions.
     * @returns {Bounds2}
     */
    getBounds() {
      return new Bounds2( 0, 0, this.width, this.height );
    }

    /**
     * Returns true if the visible bounds contains the lattice coordinate
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     */
    visibleBoundsContains( i, j ) {
      const b = this.visibleBounds;

      // Note this differs from the standard Bounds2.containsCoordinate because we must exclude right and bottom edge
      // from reading one cell off the visible lattice, see https://github.com/phetsims/wave-interference/issues/86
      return b.minX <= i && i < b.maxX && b.minY <= j && j < b.maxY;
    }

    /**
     * Returns true if the given coordinate is within the lattice
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     * @public
     */
    contains( i, j ) {
      return i >= 0 && i < this.width && j >= 0 && j < this.height;
    }

    /**
     * Read the values on the center line of the lattice (omits the out-of-bounds damping regions), for display in the
     * WaveAreaGraphNode
     * @param {number[]} array - array to fill with the values for performance/memory, will be resized if necessary
     * @public
     */
    getCenterLineValues( array ) {
      const samplingWidth = this.width - this.dampX * 2;

      // Resize array if necessary
      if ( array.length !== samplingWidth ) {
        array.length = 0;
      }
      const samplingVerticalLocation = Util.roundSymmetric( this.height / 2 );
      for ( let i = 0; i < this.width - this.dampX * 2; i++ ) {
        array[ i ] = this.getCurrentValue( i + this.dampX, samplingVerticalLocation );
      }
    }

    /**
     * Returns the current value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getCurrentValue( i, j ) {
      return this.matrices[ this.currentMatrixIndex ].get( i, j );
    }

    /**
     * Returns the interpolated value of the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getInterpolatedValue( i, j ) {
      const currentValue = this.getCurrentValue( i, j );
      const lastValue = this.getLastValue( i, j );
      return currentValue * this.interpolationRatio + lastValue * ( 1 - this.interpolationRatio );
    }

    /**
     * Sets the current value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @public
     */
    setCurrentValue( i, j, value ) {
      this.matrices[ this.currentMatrixIndex ].set( i, j, value );
    }

    /**
     * Returns the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @private
     */
    getLastValue( i, j ) {
      return this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].get( i, j );
    }

    /**
     * Sets the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @private
     */
    setLastValue( i, j, value ) {
      this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].set( i, j, value );
    }

    /**
     * Determines whether the incoming wave has reached the cell.
     * @param {number} i - horizontal coordinate to check
     * @param {number} j - vertical coordinate to check
     * @returns {boolean}
     * @public
     */
    hasCellBeenVisited( i, j ) {
      return this.visitedMatrix.get( i, j ) === 1;
    }

    /**
     * Resets all of the wave values to 0
     * @public
     */
    clear() {
      this.matrices.forEach( matrix => matrix.timesEquals( 0 ) );
      this.visitedMatrix.timesEquals( 0 );
    }

    /**
     * Gets the values on the right hand side of the wave (before the damping region), for determining intensity.
     * @returns {number[]}
     * @public
     */
    getOutputColumn() {

      // This could be implemented in garbage-free from by require preallocating the entire intensitySample matrix and
      // using an index pointer like a circular array.  However, profiling in Mac Chrome did not show a significant
      // amount of time spent in this function, hence we use the simpler implementation.
      const column = [];
      for ( let j = this.dampY; j < this.height - this.dampY; j++ ) {
        const a = this.getCurrentValue( this.width - this.dampX - 1, j );
        const b = this.getCurrentValue( this.width - this.dampX - 2, j );
        const v = ( a + b ) / 2;
        column.push( v );
      }
      return column;
    }

    /**
     * Propagates the wave by one step.  This is a discrete algorithm and cannot use dt.
     * @param {function} forcingFunction - sets values before and after the wave equation
     * @public
     */
    step( forcingFunction ) {

      // Move to the next matrix
      this.currentMatrixIndex = ( this.currentMatrixIndex - 1 + this.matrices.length ) % this.matrices.length;

      const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ];
      const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ];
      const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ];
      const width = matrix0.getRowDimension();
      const height = matrix0.getColumnDimension();

      // Main loop, doesn't update cells on the edges
      for ( let i = 1; i < width - 1; i++ ) {
        for ( let j = 1; j < height - 1; j++ ) {
          const neighborSum = matrix1.get( i + 1, j ) +
                              matrix1.get( i - 1, j ) +
                              matrix1.get( i, j + 1 ) +
                              matrix1.get( i, j - 1 );
          const m1ij = matrix1.get( i, j );
          const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
          matrix0.set( i, j, value );

          if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
            this.visitedMatrix.set( i, j, 1 );
          }
        }
      }

      // From https://www.phy.ornl.gov/csep/sw/node22.html
      // Note there is a fortran error on the top boundary
      // u2 => matrix1.get
      // u1 => matrix2.get
      // cb => k

      var k = WAVE_SPEED;

      // Left edge
      let i = 0;
      for ( let j = 1; j < height - 1; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + k *
                    ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Right edge
      i = width - 1;
      for ( let j = 1; j < height - 1; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + k *
                    ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Top edge
      let j = 0;
      for ( let i = 1; i < width - 1; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + k *
                    ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
        matrix0.set( i, j, sum );
      }

      // Bottom edge
      j = height - 1;
      for ( let i = 1; i < width - 1; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + k *
                    ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
        matrix0.set( i, j, sum );
      }

      // Apply values on top of the computed lattice values so there is no noise at the point sources
      forcingFunction();
    }
  }

  return waveInterference.register( 'Lattice', Lattice );
} );<commit_msg>Visit entire edge, see https://github.com/phetsims/wave-interference/issues/197<commit_after>// Copyright 2017-2018, University of Colorado Boulder

/**
 * The lattice is a 2D grid with a value in each cell that represents the wave amplitude at that point.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const Bounds2 = require( 'DOT/Bounds2' );
  const Emitter = require( 'AXON/Emitter' );
  const Matrix = require( 'DOT/Matrix' );
  const Util = require( 'DOT/Util' );
  const waveInterference = require( 'WAVE_INTERFERENCE/waveInterference' );

  // constants

  // The wave speed in the coordinate frame of the lattice, see http://www.mtnmath.com/whatth/node47.html. We tried
  // different values, but they do not have the properer emergent behavior.  WAVE_SPEED=1 propagates out as a diamond
  // rather than a circle, and WAVE_SPEED=0.1 is too slow and throws off the frequency of light.
  const WAVE_SPEED = 0.5;
  const WAVE_SPEED_SQUARED = WAVE_SPEED * WAVE_SPEED; // precompute to avoid work in the inner loop
  const NUMBER_OF_MATRICES = 3; // The discretized wave equation algorithm requires current value + 2 history points

  // This is the threshold for the wave value that determines if the light has visited.  If the value is higher,
  // it will track the wavefront of the light more accurately (and hence could be used for more accurate computation of
  // the speed of light), but will generate more artifacts in the initial wave.  If the value is lower, it will generate
  // fewer artifacts in the initial propagation, but will lead the initial wavefront by too far and make it seem like
  // light is faster than it should be measured (based on the propagation of wavefronts).
  const LIGHT_VISIT_THRESHOLD = 0.06;

  class Lattice {

    /**
     * @param {number} width - width of the lattice
     * @param {number} height - height of the lattice
     * @param {number} dampX - number of cells on the left and again on the right to use for damping
     * @param {number} dampY - number of cells on the top and again on the bottom to use for damping
     */
    constructor( width, height, dampX, dampY ) {

      // @public (read-only) {number} - number of cells on the left and again on the right to use for damping
      this.dampX = dampX;

      // @public (read-only) {number} - number of cells on the top and again on the bottom to use for damping
      this.dampY = dampY;

      // @private {Matrix[]} - matrices for current value, previous value and value before previous
      this.matrices = [];
      for ( let i = 0; i < NUMBER_OF_MATRICES; i++ ) {
        this.matrices.push( new Matrix( width, height ) );
      }

      // @private {Matrix} - keeps track of which cells have been visited by the wave
      this.visitedMatrix = new Matrix( width, height );

      // @private {number} - indicates the current matrix. Previous matrix is one higher (with correct modulus)
      this.currentMatrixIndex = 0;

      // @public {Emitter} - sends a notification each time the lattice updates.
      this.changedEmitter = new Emitter();

      // @public (read-only) {number} - width of the lattice (includes damping regions)
      this.width = width;

      // @public (read-only) {number} - height of the lattice (includes damping regions)
      this.height = height;

      // @public {number} - Determines how far we have animated between the "last" and "current" matrices, so that we
      // can use getInterpolatedValue to update the view at 60fps even though the model is running at a slower rate
      this.interpolationRatio = 0;

      // @public (read-only) {Bounds2} - a Bounds2 representing the visible (non-damping) region of the lattice.
      this.visibleBounds = new Bounds2( this.dampX, this.dampY, this.width - this.dampX, this.height - this.dampY );
    }

    /**
     * Gets a Bounds2 representing the full region of the lattice, including damping regions.
     * @returns {Bounds2}
     */
    getBounds() {
      return new Bounds2( 0, 0, this.width, this.height );
    }

    /**
     * Returns true if the visible bounds contains the lattice coordinate
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     */
    visibleBoundsContains( i, j ) {
      const b = this.visibleBounds;

      // Note this differs from the standard Bounds2.containsCoordinate because we must exclude right and bottom edge
      // from reading one cell off the visible lattice, see https://github.com/phetsims/wave-interference/issues/86
      return b.minX <= i && i < b.maxX && b.minY <= j && j < b.maxY;
    }

    /**
     * Returns true if the given coordinate is within the lattice
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     * @public
     */
    contains( i, j ) {
      return i >= 0 && i < this.width && j >= 0 && j < this.height;
    }

    /**
     * Read the values on the center line of the lattice (omits the out-of-bounds damping regions), for display in the
     * WaveAreaGraphNode
     * @param {number[]} array - array to fill with the values for performance/memory, will be resized if necessary
     * @public
     */
    getCenterLineValues( array ) {
      const samplingWidth = this.width - this.dampX * 2;

      // Resize array if necessary
      if ( array.length !== samplingWidth ) {
        array.length = 0;
      }
      const samplingVerticalLocation = Util.roundSymmetric( this.height / 2 );
      for ( let i = 0; i < this.width - this.dampX * 2; i++ ) {
        array[ i ] = this.getCurrentValue( i + this.dampX, samplingVerticalLocation );
      }
    }

    /**
     * Returns the current value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getCurrentValue( i, j ) {
      return this.matrices[ this.currentMatrixIndex ].get( i, j );
    }

    /**
     * Returns the interpolated value of the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getInterpolatedValue( i, j ) {
      const currentValue = this.getCurrentValue( i, j );
      const lastValue = this.getLastValue( i, j );
      return currentValue * this.interpolationRatio + lastValue * ( 1 - this.interpolationRatio );
    }

    /**
     * Sets the current value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @public
     */
    setCurrentValue( i, j, value ) {
      this.matrices[ this.currentMatrixIndex ].set( i, j, value );
    }

    /**
     * Returns the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @private
     */
    getLastValue( i, j ) {
      return this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].get( i, j );
    }

    /**
     * Sets the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @private
     */
    setLastValue( i, j, value ) {
      this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].set( i, j, value );
    }

    /**
     * Determines whether the incoming wave has reached the cell.
     * @param {number} i - horizontal coordinate to check
     * @param {number} j - vertical coordinate to check
     * @returns {boolean}
     * @public
     */
    hasCellBeenVisited( i, j ) {
      return this.visitedMatrix.get( i, j ) === 1;
    }

    /**
     * Resets all of the wave values to 0
     * @public
     */
    clear() {
      this.matrices.forEach( matrix => matrix.timesEquals( 0 ) );
      this.visitedMatrix.timesEquals( 0 );
    }

    /**
     * Gets the values on the right hand side of the wave (before the damping region), for determining intensity.
     * @returns {number[]}
     * @public
     */
    getOutputColumn() {

      // This could be implemented in garbage-free from by require preallocating the entire intensitySample matrix and
      // using an index pointer like a circular array.  However, profiling in Mac Chrome did not show a significant
      // amount of time spent in this function, hence we use the simpler implementation.
      const column = [];
      for ( let j = this.dampY; j < this.height - this.dampY; j++ ) {
        const a = this.getCurrentValue( this.width - this.dampX - 1, j );
        const b = this.getCurrentValue( this.width - this.dampX - 2, j );
        const v = ( a + b ) / 2;
        column.push( v );
      }
      return column;
    }

    /**
     * Propagates the wave by one step.  This is a discrete algorithm and cannot use dt.
     * @param {function} forcingFunction - sets values before and after the wave equation
     * @public
     */
    step( forcingFunction ) {

      // Move to the next matrix
      this.currentMatrixIndex = ( this.currentMatrixIndex - 1 + this.matrices.length ) % this.matrices.length;

      const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ];
      const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ];
      const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ];
      const width = matrix0.getRowDimension();
      const height = matrix0.getColumnDimension();

      // Main loop, doesn't update cells on the edges
      for ( let i = 1; i < width - 1; i++ ) {
        for ( let j = 1; j < height - 1; j++ ) {
          const neighborSum = matrix1.get( i + 1, j ) +
                              matrix1.get( i - 1, j ) +
                              matrix1.get( i, j + 1 ) +
                              matrix1.get( i, j - 1 );
          const m1ij = matrix1.get( i, j );
          const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
          matrix0.set( i, j, value );

          if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
            this.visitedMatrix.set( i, j, 1 );
          }
        }
      }

      // Numerical computation of absorbing boundary conditions, (incorrectly) assuming that the wave is perpendicular
      // to the edge, see https://www.phy.ornl.gov/csep/sw/node22.html
      // Note there is a fortran error on the top boundary and in the equations, replace:
      // u2 => matrix1.get
      // u1 => matrix2.get
      // cb => WAVE_SPEED

      // Left edge
      let i = 0;
      for ( let j = 0; j < height; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
                    ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Right edge
      i = width - 1;
      for ( let j = 0; j < height; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
                    ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Top edge
      let j = 0;
      for ( let i = 0; i < width; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
                    ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
        matrix0.set( i, j, sum );
      }

      // Bottom edge
      j = height - 1;
      for ( let i = 0; i < width; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
                    ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
        matrix0.set( i, j, sum );
      }

      // Apply values on top of the computed lattice values so there is no noise at the point sources
      forcingFunction();
    }
  }

  return waveInterference.register( 'Lattice', Lattice );
} );<|endoftext|>


<commit_before>$(document).ready(function(){
 var refresh = $("#inptRefresh").val();
 refresh = (refresh * 1000);
 $("#add_widget").button();

 $("#tabs").tabs({ // First, Initialize tabs and the tab template
  tabTemplate: '
#{label} Remove Tab
',
  load: function(event, ui){ // When the tab is loaded
   add_features();  // Add features
  },
  select: function(event, ui){ // When a tab is selected
   $("#monitors").remove(); // Remove old monitors
  }
 });
 load_tabs(); // Then load the tabs into #tabs
 add_tab_click(); // Finally, setup the add tab feature

 function load_tabs() {
  $.post("skins/new/includes/updateGroups.php?action=select", function(data) { // Load tabs (groups)
   saved_tabs = data.split(",");
   saved_tabs.pop();
   var x = saved_tabs.length;
   for (var i=0;i<x;i++) {
    var name = saved_tabs[i];
    $("#tabs").tabs('add', 'skins/new/views/monitors.php?groupName='+name, name); // Add the tab
    }
   init_delete_tab();
   }
  );
 };

 function init_delete_tab() {
  // close icon: removing the tab on click
  // note: closable tabs gonna be an option in the future - see http://dev.jqueryui.com/ticket/3924
  $('#tabs .ui-icon-close').click(function() {
   var index = $('li',$("#tabs")).index($(this).parent());
   $.post("skins/new/includes/updateGroups.php?action=delete&groupName=" + $(this).parent().find('a').text());
   $("#tabs").tabs('remove', index);
  });
 }

 function add_features() {
  // This function is called whenever cameras are loaded (via the tab load event)
  // It setups up Minimize, Sortable, Colorbox features for the newly-loaded monitors
  $(".minimize").click(function() { // Minimize
   $(this).parent().parent().parent().find('.mon').toggle('blind');
  });

  $("#monitors").sortable({ opacity: 0.6, cursor: 'move', update: function() { // Sortable
   var order = $(this).sortable("serialize") + '&action=sequence';
   $.post("skins/new/includes/updateSequence.php", order);
  }});

  $("a[rel='monitor']").colorbox({ // Colorbox
   iframe:true,
   preloading:false,
   current:'{current} of {total}',
   width:'85%',
   height:'95%'
  });
 };  // END ADD_FEATURES //

 function add_tab_click() { // Call this last
  // This whole shabang is used for adding tabs.
  // We get the tab title and contents, setup the dialog box, create the functions
  // for adding the tab, deleting tab, closing and opening dialog box, etc.

  var $tab_title_input = $('#tab_title'), $tab_content_input = $('#tab_content');

  // modal dialog init: custom buttons and a "close" callback reseting the form inside
  var $dialog = $('#dialog').dialog({
   autoOpen: false,
   modal: true,
   buttons: {
    'Add': function() {
      addTab();
      $(this).dialog('close');
     },
    'Cancel': function() {
     $(this).dialog('close');
    }
   },
   open: function() {
    tab_title_input.focus();
   }
  }); // END DIALOG //

  // addTab form: calls addTab function on submit and closes the dialog
  var $form = $('form',$dialog).submit(function() {
  addTab();
  $dialog.dialog('close');
  return false;
 });

  // actual addTab function: adds new tab using the title input from the form above
  function addTab() {
   tab_title = $tab_title_input.val(); // groupName
   var arysel  = []; // An array for the monitorIds
   $("#selMonitors :selected").each(function(i, selected) { // For each selected MonitorId
    arysel[i] = $(selected).val(); // Put it into the array
   });
   var mids = arysel.toString(); // Make a comman-separated list of the select MonitorsIds
   $.post("skins/new/includes/updateGroups.php?groupName=" + tab_title + "&mids=" + mids + "&action=insert"); // Add the new Group
   $("#tabs").tabs('add', 'skins/new/views/monitors.php?groupName='+tab_title, tab_title); // Add the actual tab
   init_delete_tab();
  }

  // addTab button: just opens the dialog
  $('#add_tab').button().click(function() {
   $dialog.dialog('open');
  });

 } // END ADD_TAB_CLICK() //

  setInterval(function() {
   $("#monitors li").each(function() {
   var _this = $(this);
   $(".spinner",_this).html("");
   var mid = $(this).attr("id");
   mid = mid.split("_");
   $(".mon",this).load("skins/new/views/monitors.php?mid=" + mid[1] + " .mon", function () {
    $(".spinner",_this).fadeOut('slow');
   });
  });
 }, refresh);

 });
<commit_msg>Fixed some indentation for easier reading...  I have not looked at this code in a while!<commit_after>$(document).ready(function(){
 var refresh = $("#inptRefresh").val();
 refresh = (refresh * 1000);
 $("#add_widget").button();

 $("#tabs").tabs({ // First, Initialize tabs and the tab template
  tabTemplate: '
#{label} Remove Tab
',
  load: function(event, ui){ // When the tab is loaded
   add_features();  // Add features
  },
  select: function(event, ui){ // When a tab is selected
   $("#monitors").remove(); // Remove old monitors
  }
 });
 load_tabs(); // Then load the tabs into #tabs
 add_tab_click(); // Finally, setup the add tab feature

 function load_tabs() {
  $.post("skins/new/includes/updateGroups.php?action=select", function(data) { // Load tabs (groups)
   saved_tabs = data.split(",");
   saved_tabs.pop();
   var x = saved_tabs.length;
   for (var i=0;i<x;i++) {
    var name = saved_tabs[i];
    $("#tabs").tabs('add', 'skins/new/views/monitors.php?groupName='+name, name); // Add the tab
    }
   init_delete_tab();
   }
  );
 };

 function init_delete_tab() {
  // close icon: removing the tab on click
  // note: closable tabs gonna be an option in the future - see http://dev.jqueryui.com/ticket/3924
  $('#tabs .ui-icon-close').click(function() {
   var index = $('li',$("#tabs")).index($(this).parent());
   $.post("skins/new/includes/updateGroups.php?action=delete&groupName=" + $(this).parent().find('a').text());
   $("#tabs").tabs('remove', index);
  });
 }

 function add_features() {
  // This function is called whenever cameras are loaded (via the tab load event)
  // It setups up Minimize, Sortable, Colorbox features for the newly-loaded monitors
  $(".minimize").click(function() { // Minimize
   $(this).parent().parent().parent().find('.mon').toggle('blind');
  });

  $("#monitors").sortable({ opacity: 0.6, cursor: 'move', update: function() { // Sortable
   var order = $(this).sortable("serialize") + '&action=sequence';
   $.post("skins/new/includes/updateSequence.php", order);
  }});

  $("a[rel='monitor']").colorbox({ // Colorbox
   iframe:true,
   preloading:false,
   current:'{current} of {total}',
   width:'85%',
   height:'95%'
  });
 };  // END ADD_FEATURES //

 function add_tab_click() { // Call this last
  // This whole shabang is used for adding tabs.
  // We get the tab title and contents, setup the dialog box, create the functions
  // for adding the tab, deleting tab, closing and opening dialog box, etc.

  var $tab_title_input = $('#tab_title'), $tab_content_input = $('#tab_content');

  // modal dialog init: custom buttons and a "close" callback reseting the form inside
  var $dialog = $('#dialog').dialog({
   autoOpen: false,
   modal: true,
   buttons: {
    'Add': function() {
      addTab();
      $(this).dialog('close');
     },
    'Cancel': function() {
     $(this).dialog('close');
    }
   },
   open: function() {
    tab_title_input.focus();
   }
  }); // END DIALOG //

  // addTab form: calls addTab function on submit and closes the dialog
  var $form = $('form',$dialog).submit(function() {
  addTab();
  $dialog.dialog('close');
  return false;
 });

  // actual addTab function: adds new tab using the title input from the form above
  function addTab() {
   tab_title = $tab_title_input.val(); // groupName
   var arysel  = []; // An array for the monitorIds
   $("#selMonitors :selected").each(function(i, selected) { // For each selected MonitorId
    arysel[i] = $(selected).val(); // Put it into the array
   });
   var mids = arysel.toString(); // Make a comman-separated list of the select MonitorsIds
   $.post("skins/new/includes/updateGroups.php?groupName=" + tab_title + "&mids=" + mids + "&action=insert"); // Add the new Group
   $("#tabs").tabs('add', 'skins/new/views/monitors.php?groupName='+tab_title, tab_title); // Add the actual tab
   init_delete_tab();
  }

  // addTab button: just opens the dialog
  $('#add_tab').button().click(function() {
   $dialog.dialog('open');
  });

 } // END ADD_TAB_CLICK() //

  setInterval(function() {
   $("#monitors li").each(function() {
    var _this = $(this);
    $(".spinner",_this).html("");
    var mid = $(this).attr("id");
    mid = mid.split("_");

    $(".mon",this).load("skins/new/views/monitors.php?mid=" + mid[1] + " .mon", function () {
     $(".spinner",_this).fadeOut('slow');
    });
  });
 }, refresh);

 });
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Relay from 'react-relay';
import { Alert, Modal, Form, FormGroup, FieldGroup, Col, Button } from 'react-bootstrap';

import UpdatePhoneMutation from '../mutations/UpdatePhoneMutation';

export default class EditModal extends Component {

  constructor(props) {
    super(props);
    this.state = {
      error: false,
    };
  }

  close = () => {
    const { handleEditModal } = this.props;

    handleEditModal(false);
  };

  updatePhone = () => {
    const { viewer, editingPhone } = this.props;
    const { phoneModelInput, phoneImageInput } = this.refs;

    const phoneId = editingPhone.phoneId;

    Relay.Store.commitUpdate(
      new UpdatePhoneMutation({
        viewer,
        phoneId,
        phoneModel: phoneModelInput.value,
        phoneImage: phoneImageInput.value,
      }),
      {
        onFailure: (transaction) => {
          this.setState({
            error: 'Something went wrong, please try again.',
          });
        },
        onSuccess: (response) => {
          this.close();
        },
      },
    );
  };

  renderError() {
    const { error } = this.state;

    if (!error) {
      return null;
    }

    return (
      <Alert bsStyle="danger">
        {error}
      </Alert>
    );
  }

  render() {
    const { editingPhone } = this.props;

    /**
     * Not going to use <FormControl> bc I want my inputs to be
     * uncontrollable components.
     */
    return (
      <Modal.Dialog style={Style.container}>
        <Modal.Header closeButton onClick={this.close}>
          <Modal.Title>Editing phone {editingPhone.phoneModel}</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          {this.renderError()}
          <Form horizontal>
            <FormGroup controlId="phoneModelControl">
             <Col sm={12}>
               <input className="form-control" ref="phoneModelInput" type="text" placeholder="Phone Model" />
             </Col>
            </FormGroup>
            <FormGroup controlId="phoneImageControl">
             <Col sm={12}>
               <input className="form-control" ref="phoneImageInput" type="text" placeholder="Phone Image" />
             </Col>
            </FormGroup>
            <FormGroup>
             <Col sm={12}>
               <Button
                 style={Style.addButton}
                 type="button"
                 onClick={() => this.updatePhone()}
                >
                 Update
               </Button>
             </Col>
            </FormGroup>
          </Form>

Sample images:

{`iPhone 6 - https://goo.gl/ndJdW9`}

{`Galaxy S7 - https://goo.gl/uanrHM`}

{`Moto X - https://goo.gl/690VPe`}

{`Nexus 5 - https://goo.gl/Fq46CZ`}

{`Zenfone 3 - https://goo.gl/w2e5xX`}

        </Modal.Body>
      </Modal.Dialog>
    );
  }
}

const Style = {
  container: {
    backgroundColor: 'rgba(47, 46, 46, 0.71)',
  },
  addButton: {
    float: 'right',
  },
  imageTips: {
    color: '#656565',
    fontSize: '13px',
    fontStyle: 'italic',
  },
};

EditModal.propTypes = {
  viewer: React.PropTypes.object,
  editingPhone: React.PropTypes.object,
  handleEditModal: React.PropTypes.func,
};
<commit_msg>Update modal title on edit modal<commit_after>import React, { Component } from 'react';
import Relay from 'react-relay';
import { Alert, Modal, Form, FormGroup, FieldGroup, Col, Button } from 'react-bootstrap';

import UpdatePhoneMutation from '../mutations/UpdatePhoneMutation';

export default class EditModal extends Component {

  constructor(props) {
    super(props);
    this.state = {
      error: false,
    };
  }

  close = () => {
    const { handleEditModal } = this.props;

    handleEditModal(false);
  };

  updatePhone = () => {
    const { viewer, editingPhone } = this.props;
    const { phoneModelInput, phoneImageInput } = this.refs;

    const phoneId = editingPhone.phoneId;

    Relay.Store.commitUpdate(
      new UpdatePhoneMutation({
        viewer,
        phoneId,
        phoneModel: phoneModelInput.value,
        phoneImage: phoneImageInput.value,
      }),
      {
        onFailure: (transaction) => {
          this.setState({
            error: 'Something went wrong, please try again.',
          });
        },
        onSuccess: (response) => {
          this.close();
        },
      },
    );
  };

  renderError() {
    const { error } = this.state;

    if (!error) {
      return null;
    }

    return (
      <Alert bsStyle="danger">
        {error}
      </Alert>
    );
  }

  render() {
    const { editingPhone } = this.props;

    /**
     * Not going to use <FormControl> bc I want my inputs to be
     * uncontrollable components.
     */
    return (
      <Modal.Dialog style={Style.container}>
        <Modal.Header closeButton onClick={this.close}>
          <Modal.Title>Editing {editingPhone.phoneModel}</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          {this.renderError()}
          <Form horizontal>
            <FormGroup controlId="phoneModelControl">
             <Col sm={12}>
               <input className="form-control" ref="phoneModelInput" type="text" placeholder="Phone Model" />
             </Col>
            </FormGroup>
            <FormGroup controlId="phoneImageControl">
             <Col sm={12}>
               <input className="form-control" ref="phoneImageInput" type="text" placeholder="Phone Image" />
             </Col>
            </FormGroup>
            <FormGroup>
             <Col sm={12}>
               <Button
                 style={Style.addButton}
                 type="button"
                 onClick={() => this.updatePhone()}
                >
                 Update
               </Button>
             </Col>
            </FormGroup>
          </Form>

Sample images:

{`iPhone 6 - https://goo.gl/ndJdW9`}

{`Galaxy S7 - https://goo.gl/uanrHM`}

{`Moto X - https://goo.gl/690VPe`}

{`Nexus 5 - https://goo.gl/Fq46CZ`}

{`Zenfone 3 - https://goo.gl/w2e5xX`}

        </Modal.Body>
      </Modal.Dialog>
    );
  }
}

const Style = {
  container: {
    backgroundColor: 'rgba(47, 46, 46, 0.71)',
  },
  addButton: {
    float: 'right',
  },
  imageTips: {
    color: '#656565',
    fontSize: '13px',
    fontStyle: 'italic',
  },
};

EditModal.propTypes = {
  viewer: React.PropTypes.object,
  editingPhone: React.PropTypes.object,
  handleEditModal: React.PropTypes.func,
};
<|endoftext|>