<commit_before>'use strict';

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2010-2013 triAGENS GmbH, Cologne, Germany
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Alan Plum
////////////////////////////////////////////////////////////////////////////////

const fs = require('fs');
const joi = require('joi');
const dd = require('dedent');
const marked = require('marked');
const highlightAuto = require('highlight.js').highlightAuto;
const errors = require('@arangodb').errors;
const FoxxManager = require('@arangodb/foxx/manager');
const fmUtils = require('@arangodb/foxx/manager-utils');
const createRouter = require('@arangodb/foxx/router');

const DEFAULT_THUMBNAIL = module.context.fileName('default-thumbnail.png');

const router = createRouter();
module.exports = router;

router.use((req, res, next) => {
  if (global.AUTHENTICATION_ENABLED()) {
    if (!req.user) {
      res.throw('unauthorized');
    }
  }
  next();
});

const foxxRouter = createRouter();
router.use(foxxRouter)
.queryParam('mount', joi.string().required(), dd`
  The mount point of the service. Has to be url-encoded.
`);

const installer = createRouter();
foxxRouter.use(installer)
.queryParam('upgrade', joi.boolean().default(false), dd`
  Flag to upgrade the service installed at the mountpoint.
  Triggers setup.
`)
.queryParam('replace', joi.boolean().default(false), dd`
  Flag to replace the service installed at the mountpoint.
  Triggers teardown and setup.
`);

installer.use(function (req, res, next) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const upgrade = req.queryParams.upgrade;
  const replace = req.queryParams.replace;
  next();
  let appInfo, options;
  if (typeof req.body === 'object') {
    appInfo = 'EMPTY';
    options = req.body;
  } else {
    appInfo = req.body;
    options = undefined;
  }
  let service;
  try {
    if (upgrade) {
      service = FoxxManager.upgrade(appInfo, mount, options);
    } else if (replace) {
      service = FoxxManager.replace(appInfo, mount, options);
    } else {
      service = FoxxManager.install(appInfo, mount, options);
    }
  } catch (e) {
    if (e.isArangoError && [
      errors.ERROR_MODULE_FAILURE.code,
      errors.ERROR_MALFORMED_MANIFEST_FILE.code,
      errors.ERROR_INVALID_APPLICATION_MANIFEST.code
    ].indexOf(e.errorNum) !== -1) {
      res.throw('bad request', e);
    }
    if (
      e.isArangoError &&
      e.errorNum === errors.ERROR_APP_NOT_FOUND.code
    ) {
      res.throw('not found', e);
    }
    if (
      e.isArangoError &&
      e.errorNum === errors.ERROR_APP_MOUNTPOINT_CONFLICT.code
    ) {
      res.throw('conflict', e);
    }
    throw e;
  }
  const configuration = FoxxManager.configuration(mount);
  res.json(Object.assign(
    {error: false, configuration},
    service
  ));
});

installer.put('/store', function (req) {
  req.body = `${req.body.name}:${req.body.version}`;
})
.body(joi.object({
  name: joi.string().required(),
  version: joi.string().required()
}).required(), 'A Foxx service from the store.')
.summary('Install a Foxx from the store')
.description(dd`
  Downloads a Foxx from the store and installs it at the given mount.
`);

installer.put('/git', function (req) {
  req.body = `git:${req.body.url}:${req.body.version}`;
})
.body(joi.object({
  url: joi.string().required(),
  version: joi.string().default('master')
}).required(), 'A GitHub reference.')
.summary('Install a Foxx from Github')
.description(dd`
  Install a Foxx with user/repository and version.
`);

installer.put('/generate', () => {})
.body(joi.object().required(), 'A Foxx generator configuration.')
.summary('Generate a new foxx')
.description(dd`
  Generate a new empty foxx on the given mount point.
`);

installer.put('/zip', function (req) {
  req.body = req.body.zipFile;
})
.body(joi.object({
  zipFile: joi.string().required()
}).required(), 'A zip file path.')
.summary('Install a Foxx from temporary zip file')
.description(dd`
  Install a Foxx from the given zip path.
  This path has to be created via _api/upload.
`);

foxxRouter.delete('/', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const runTeardown = req.queryParams.teardown;
  const service = FoxxManager.uninstall(mount, {
    teardown: runTeardown,
    force: true
  });
  res.json(Object.assign(
    {error: false},
    service
  ));
})
.queryParam('teardown', joi.boolean().default(true))
.summary('Uninstall a Foxx')
.description(dd`
  Uninstall the Foxx at the given mount-point.
`);

router.get('/', function (req, res) {
  const foxxes = FoxxManager.listJson();
  foxxes.forEach((foxx) => {
    const readme = FoxxManager.readme(foxx.mount);
    if (readme) {
      foxx.readme = marked(readme, {
        highlight: (code) => highlightAuto(code).value
      });
    }
  });
  res.json(foxxes);
})
.summary('List all Foxxes')
.description(dd`
  Get a List of all running foxxes.
`);

foxxRouter.get('/thumbnail', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const service = FoxxManager.lookupService(mount);
  res.sendFile(service.thumbnail || DEFAULT_THUMBNAIL);
})
.summary('Get the thumbnail of a Foxx')
.description(dd`
  Request the thumbnail of the given Foxx in order to display it on the screen.
`);

foxxRouter.get('/config', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  res.json(FoxxManager.configuration(mount));
})
.summary('Get the configuration for a service')
.description(dd`
  Used to request the configuration options for services.
`);

foxxRouter.patch('/config', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const configuration = req.body;
  res.json(FoxxManager.configure(mount, {configuration}));
})
.body(joi.object().optional(), 'Configuration to apply.')
.summary('Set the configuration for a service')
.description(dd`
  Used to overwrite the configuration options for services.
`);

foxxRouter.get('/deps', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  res.json(FoxxManager.dependencies(mount));
})
.summary('Get the dependencies for a service')
.description(dd`
  Used to request the dependencies options for services.
`);

foxxRouter.patch('/deps', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const dependencies = req.body;
  res.json(FoxxManager.updateDeps(mount, {dependencies}));
})
.body(joi.object().optional(), 'Dependency settings to apply.')
.summary('Set the dependencies for a service')
.description(dd`
  Used to overwrite the dependencies options for services.
`);

foxxRouter.post('/tests', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  res.json(FoxxManager.runTests(mount, req.body));
})
.body(joi.object().optional(), 'Options to pass to the test runner.')
.summary('Run tests for a service')
.description(dd`
  Used to run the tests of a service.
`);

foxxRouter.post('/scripts/:name', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const name = req.queryParams.name;
  try {
    res.json(FoxxManager.runScript(name, mount, req.body));
  } catch (e) {
    throw e.cause || e;
  }
})
.pathParam('name', joi.string().required(), 'The name of a service\'s script to run.')
.body(joi.any().default(null), 'Options to pass to the script.')
.summary('Run a script for a service')
.description(dd`
  Used to trigger any script of a service.
`);

foxxRouter.patch('/devel', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const activate = Boolean(req.body);
  res.json(FoxxManager[activate ? 'development' : 'production'](mount));
})
.body(joi.boolean().optional())
.summary('Activate/Deactivate development mode for a service')
.description(dd`
  Used to toggle between production and development mode.
`);

foxxRouter.get('/download/zip', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const service = FoxxManager.lookupService(mount);
  const dir = fs.join(fs.makeAbsolute(service.root), service.path);
  const zipPath = fmUtils.zipDirectory(dir);
  const name = mount.replace(/^\/|\/$/g, '').replace(/\//g, '_');
  res.download(zipPath, `${name}.zip`);
})
.summary('Download a service as zip archive')
.description(dd`
  Download a foxx service packed in a zip archive.
`);

router.get('/fishbowl', function (req, res) {
  try {
    FoxxManager.update();
  } catch (e) {
    console.warnLines(`Failed to update Foxx store: ${e.stack}`);
  }
  res.json(FoxxManager.availableJson());
})
.summary('List of all foxx services submitted to the Foxx store.')
.description(dd`
  This function contacts the fishbowl and reports which services are available for install.
`);

router.get('/docs/standalone/*', module.context.apiDocumentation(
  (req) => ({
    appPath: decodeURIComponent(req.queryParams.mount)
  })
));

router.get('/docs/*', module.context.apiDocumentation(
  (req) => ({
    appPath: decodeURIComponent(req.queryParams.mount),
    indexFile: 'index-alt.html'
  })
));
<commit_msg>Make documentation available<commit_after>'use strict';

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2010-2013 triAGENS GmbH, Cologne, Germany
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Alan Plum
////////////////////////////////////////////////////////////////////////////////

const fs = require('fs');
const joi = require('joi');
const dd = require('dedent');
const marked = require('marked');
const highlightAuto = require('highlight.js').highlightAuto;
const errors = require('@arangodb').errors;
const FoxxManager = require('@arangodb/foxx/manager');
const fmUtils = require('@arangodb/foxx/manager-utils');
const createRouter = require('@arangodb/foxx/router');

const DEFAULT_THUMBNAIL = module.context.fileName('default-thumbnail.png');

const anonymousRouter = createRouter();
const router = createRouter();
anonymousRouter.use(router);

module.exports = anonymousRouter;

router.use((req, res, next) => {
  if (global.AUTHENTICATION_ENABLED()) {
    if (!req.user) {
      res.throw('unauthorized');
    }
  }
  next();
});

const foxxRouter = createRouter();
router.use(foxxRouter)
.queryParam('mount', joi.string().required(), dd`
  The mount point of the service. Has to be url-encoded.
`);

const installer = createRouter();
foxxRouter.use(installer)
.queryParam('upgrade', joi.boolean().default(false), dd`
  Flag to upgrade the service installed at the mountpoint.
  Triggers setup.
`)
.queryParam('replace', joi.boolean().default(false), dd`
  Flag to replace the service installed at the mountpoint.
  Triggers teardown and setup.
`);

installer.use(function (req, res, next) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const upgrade = req.queryParams.upgrade;
  const replace = req.queryParams.replace;
  next();
  let appInfo, options;
  if (typeof req.body === 'object') {
    appInfo = 'EMPTY';
    options = req.body;
  } else {
    appInfo = req.body;
    options = undefined;
  }
  let service;
  try {
    if (upgrade) {
      service = FoxxManager.upgrade(appInfo, mount, options);
    } else if (replace) {
      service = FoxxManager.replace(appInfo, mount, options);
    } else {
      service = FoxxManager.install(appInfo, mount, options);
    }
  } catch (e) {
    if (e.isArangoError && [
      errors.ERROR_MODULE_FAILURE.code,
      errors.ERROR_MALFORMED_MANIFEST_FILE.code,
      errors.ERROR_INVALID_APPLICATION_MANIFEST.code
    ].indexOf(e.errorNum) !== -1) {
      res.throw('bad request', e);
    }
    if (
      e.isArangoError &&
      e.errorNum === errors.ERROR_APP_NOT_FOUND.code
    ) {
      res.throw('not found', e);
    }
    if (
      e.isArangoError &&
      e.errorNum === errors.ERROR_APP_MOUNTPOINT_CONFLICT.code
    ) {
      res.throw('conflict', e);
    }
    throw e;
  }
  const configuration = FoxxManager.configuration(mount);
  res.json(Object.assign(
    {error: false, configuration},
    service
  ));
});

installer.put('/store', function (req) {
  req.body = `${req.body.name}:${req.body.version}`;
})
.body(joi.object({
  name: joi.string().required(),
  version: joi.string().required()
}).required(), 'A Foxx service from the store.')
.summary('Install a Foxx from the store')
.description(dd`
  Downloads a Foxx from the store and installs it at the given mount.
`);

installer.put('/git', function (req) {
  req.body = `git:${req.body.url}:${req.body.version}`;
})
.body(joi.object({
  url: joi.string().required(),
  version: joi.string().default('master')
}).required(), 'A GitHub reference.')
.summary('Install a Foxx from Github')
.description(dd`
  Install a Foxx with user/repository and version.
`);

installer.put('/generate', () => {})
.body(joi.object().required(), 'A Foxx generator configuration.')
.summary('Generate a new foxx')
.description(dd`
  Generate a new empty foxx on the given mount point.
`);

installer.put('/zip', function (req) {
  req.body = req.body.zipFile;
})
.body(joi.object({
  zipFile: joi.string().required()
}).required(), 'A zip file path.')
.summary('Install a Foxx from temporary zip file')
.description(dd`
  Install a Foxx from the given zip path.
  This path has to be created via _api/upload.
`);

foxxRouter.delete('/', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const runTeardown = req.queryParams.teardown;
  const service = FoxxManager.uninstall(mount, {
    teardown: runTeardown,
    force: true
  });
  res.json(Object.assign(
    {error: false},
    service
  ));
})
.queryParam('teardown', joi.boolean().default(true))
.summary('Uninstall a Foxx')
.description(dd`
  Uninstall the Foxx at the given mount-point.
`);

router.get('/', function (req, res) {
  const foxxes = FoxxManager.listJson();
  foxxes.forEach((foxx) => {
    const readme = FoxxManager.readme(foxx.mount);
    if (readme) {
      foxx.readme = marked(readme, {
        highlight: (code) => highlightAuto(code).value
      });
    }
  });
  res.json(foxxes);
})
.summary('List all Foxxes')
.description(dd`
  Get a List of all running foxxes.
`);

foxxRouter.get('/thumbnail', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const service = FoxxManager.lookupService(mount);
  res.sendFile(service.thumbnail || DEFAULT_THUMBNAIL);
})
.summary('Get the thumbnail of a Foxx')
.description(dd`
  Request the thumbnail of the given Foxx in order to display it on the screen.
`);

foxxRouter.get('/config', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  res.json(FoxxManager.configuration(mount));
})
.summary('Get the configuration for a service')
.description(dd`
  Used to request the configuration options for services.
`);

foxxRouter.patch('/config', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const configuration = req.body;
  res.json(FoxxManager.configure(mount, {configuration}));
})
.body(joi.object().optional(), 'Configuration to apply.')
.summary('Set the configuration for a service')
.description(dd`
  Used to overwrite the configuration options for services.
`);

foxxRouter.get('/deps', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  res.json(FoxxManager.dependencies(mount));
})
.summary('Get the dependencies for a service')
.description(dd`
  Used to request the dependencies options for services.
`);

foxxRouter.patch('/deps', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const dependencies = req.body;
  res.json(FoxxManager.updateDeps(mount, {dependencies}));
})
.body(joi.object().optional(), 'Dependency settings to apply.')
.summary('Set the dependencies for a service')
.description(dd`
  Used to overwrite the dependencies options for services.
`);

foxxRouter.post('/tests', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  res.json(FoxxManager.runTests(mount, req.body));
})
.body(joi.object().optional(), 'Options to pass to the test runner.')
.summary('Run tests for a service')
.description(dd`
  Used to run the tests of a service.
`);

foxxRouter.post('/scripts/:name', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const name = req.queryParams.name;
  try {
    res.json(FoxxManager.runScript(name, mount, req.body));
  } catch (e) {
    throw e.cause || e;
  }
})
.pathParam('name', joi.string().required(), 'The name of a service\'s script to run.')
.body(joi.any().default(null), 'Options to pass to the script.')
.summary('Run a script for a service')
.description(dd`
  Used to trigger any script of a service.
`);

foxxRouter.patch('/devel', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const activate = Boolean(req.body);
  res.json(FoxxManager[activate ? 'development' : 'production'](mount));
})
.body(joi.boolean().optional())
.summary('Activate/Deactivate development mode for a service')
.description(dd`
  Used to toggle between production and development mode.
`);

foxxRouter.get('/download/zip', function (req, res) {
  const mount = decodeURIComponent(req.queryParams.mount);
  const service = FoxxManager.lookupService(mount);
  const dir = fs.join(fs.makeAbsolute(service.root), service.path);
  const zipPath = fmUtils.zipDirectory(dir);
  const name = mount.replace(/^\/|\/$/g, '').replace(/\//g, '_');
  res.download(zipPath, `${name}.zip`);
})
.summary('Download a service as zip archive')
.description(dd`
  Download a foxx service packed in a zip archive.
`);

router.get('/fishbowl', function (req, res) {
  try {
    FoxxManager.update();
  } catch (e) {
    console.warnLines(`Failed to update Foxx store: ${e.stack}`);
  }
  res.json(FoxxManager.availableJson());
})
.summary('List of all foxx services submitted to the Foxx store.')
.description(dd`
  This function contacts the fishbowl and reports which services are available for install.
`);

anonymousRouter.get('/docs/standalone/*', module.context.apiDocumentation(
  (req) => ({
    appPath: decodeURIComponent(req.queryParams.mount)
  })
));

anonymousRouter.get('/docs/*', module.context.apiDocumentation(
  (req) => ({
    appPath: decodeURIComponent(req.queryParams.mount),
    indexFile: 'index-alt.html'
  })
));
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'react-router'
import DocumentTitle from 'react-document-title'
import makeDocumentTitle from '../../services/documentTitle'
import './styles.scss'

class CoreLayout extends React.Component {
  // On location change, if drawer is open, close it
  componentWillUpdate (nextProps) {
    if (nextProps.location.pathname !== this.props.location.pathname) {
      if (this.refs.drawer.classList.contains('is-visible')) {
        this.refs.layout.MaterialLayout.toggleDrawer()
      }
    }
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.online !== this.props.online) {
      this.refs.snackbar.MaterialSnackbar.showSnackbar({
        message: nextProps.online ? 'You are back online' : 'You have gone offline'
      })
    }
  }

  render () {
    return (

        <DocumentTitle title={makeDocumentTitle()} />

            <Link to='/' className='mdl-layout-title'>
              EnglishAccentsMap
            </Link>

              EnglishAccentsMap

              <Link to='/' className='mdl-navigation__link' activeClassName='is-active'>
                home Home
              </Link>
              <Link to='/suggest/' className='mdl-navigation__link' activeClassName='is-active'>
                add_location Make a suggestion
              </Link>

          {this.props.children}

          <button className='mdl-snackbar__action' type='button' />

    )
  }
}

CoreLayout.propTypes = {
  location: React.PropTypes.object,
  online: React.PropTypes.bool,
  children: React.PropTypes.node
}

export default CoreLayout
<commit_msg>Class to indicate network status<commit_after>import React from 'react'
import { Link } from 'react-router'
import DocumentTitle from 'react-document-title'
import makeDocumentTitle from '../../services/documentTitle'
import './styles.scss'

class CoreLayout extends React.Component {
  // On location change, if drawer is open, close it
  componentWillUpdate (nextProps) {
    if (nextProps.location.pathname !== this.props.location.pathname) {
      if (this.refs.drawer.classList.contains('is-visible')) {
        this.refs.layout.MaterialLayout.toggleDrawer()
      }
    }
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.online !== this.props.online) {
      this.refs.snackbar.MaterialSnackbar.showSnackbar({
        message: nextProps.online ? 'You are back online' : 'You have gone offline'
      })
    }
  }

  render () {
    return (

        <DocumentTitle title={makeDocumentTitle()} />

            <Link to='/' className='mdl-layout-title'>
              EnglishAccentsMap
            </Link>

              EnglishAccentsMap

              <Link to='/' className='mdl-navigation__link' activeClassName='is-active'>
                home Home
              </Link>
              <Link to='/suggest/' className='mdl-navigation__link' activeClassName='is-active'>
                add_location Make a suggestion
              </Link>

          {this.props.children}

          <button className='mdl-snackbar__action' type='button' />

    )
  }
}

CoreLayout.propTypes = {
  location: React.PropTypes.object,
  online: React.PropTypes.bool,
  children: React.PropTypes.node
}

export default CoreLayout
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief test the collection interface w/ volatile collections
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");
var internal = require("internal");

// -----------------------------------------------------------------------------
// --SECTION--                                                collection methods
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                              volatile collections
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: volatile collections
////////////////////////////////////////////////////////////////////////////////

function CollectionVolatileSuite () {
  var ERRORS = require("internal").errors;
  var cn = "UnittestsVolatileCollection";

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      internal.db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
      internal.db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create a volatile collection
////////////////////////////////////////////////////////////////////////////////

    testCreation1 : function () {
      var c = internal.db._create(cn, { isVolatile : true, waitForSync : false });
      assertEqual(cn, c.name());
      assertEqual(true, c.properties().isVolatile);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create a volatile collection
////////////////////////////////////////////////////////////////////////////////

    testCreation2 : function () {
      var c = internal.db._create(cn, { isVolatile : true });
      assertEqual(cn, c.name());
      assertEqual(true, c.properties().isVolatile);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create w/ error
////////////////////////////////////////////////////////////////////////////////

    testCreationError : function () {
      try {
        // cannot set isVolatile and waitForSync at the same time
        var c = internal.db._create(cn, { isVolatile : true, waitForSync : true });
        fail();
      }
      catch (err) {
        assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test property change
////////////////////////////////////////////////////////////////////////////////

    testPropertyChange : function () {
      var c = internal.db._create(cn, { isVolatile : true, waitForSync : false });

      try {
        // cannot set isVolatile and waitForSync at the same time
        c.properties({ waitForSync : true });
        fail();
      }
      catch (err) {
        assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief load/unload
////////////////////////////////////////////////////////////////////////////////

    testLoadUnload : function () {
      var c = internal.db._create(cn, { isVolatile : true });
      assertEqual(cn, c.name());
      assertEqual(true, c.properties().isVolatile);
      c.unload();

      internal.wait(4);
      assertEqual(true, c.properties().isVolatile);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief data storage
////////////////////////////////////////////////////////////////////////////////

    testStorage : function () {
      var c = internal.db._create(cn, { isVolatile : true });
      assertEqual(true, c.properties().isVolatile);

      c.save({"test": true});
      assertEqual(1, c.count());
      c.unload();

      internal.wait(4);
      assertEqual(true, c.properties().isVolatile);
      assertEqual(0, c.count());
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief data storage
////////////////////////////////////////////////////////////////////////////////

    testStorageMany : function () {
      var c = internal.db._create(cn, { isVolatile : true, journalSize: 1024 * 1024 });
      assertEqual(true, c.properties().isVolatile);

      for (var i = 0; i < 10000; ++i) {
        c.save({"test": true, "foo" : "bar"});
      }

      assertEqual(10000, c.count());

      c.unload();

      internal.wait(4);
      assertEqual(true, c.properties().isVolatile);
      assertEqual(0, c.count());
    }

  };
}

// -----------------------------------------------------------------------------
// --SECTION--                                                              main
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suites
////////////////////////////////////////////////////////////////////////////////

jsunity.run(CollectionVolatileSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

<commit_msg>added test case<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief test the collection interface w/ volatile collections
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");
var internal = require("internal");

// -----------------------------------------------------------------------------
// --SECTION--                                                collection methods
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                              volatile collections
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: volatile collections
////////////////////////////////////////////////////////////////////////////////

function CollectionVolatileSuite () {
  var ERRORS = require("internal").errors;
  var cn = "UnittestsVolatileCollection";

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      internal.db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
      internal.db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create a volatile collection
////////////////////////////////////////////////////////////////////////////////

    testCreation1 : function () {
      var c = internal.db._create(cn, { isVolatile : true, waitForSync : false });
      assertEqual(cn, c.name());
      assertEqual(true, c.properties().isVolatile);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create a volatile collection
////////////////////////////////////////////////////////////////////////////////

    testCreation2 : function () {
      var c = internal.db._create(cn, { isVolatile : true });
      assertEqual(cn, c.name());
      assertEqual(true, c.properties().isVolatile);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create w/ error
////////////////////////////////////////////////////////////////////////////////

    testCreationError : function () {
      try {
        // cannot set isVolatile and waitForSync at the same time
        var c = internal.db._create(cn, { isVolatile : true, waitForSync : true });
        fail();
      }
      catch (err) {
        assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test property change
////////////////////////////////////////////////////////////////////////////////

    testPropertyChange : function () {
      var c = internal.db._create(cn, { isVolatile : true, waitForSync : false });

      try {
        // cannot set isVolatile and waitForSync at the same time
        c.properties({ waitForSync : true });
        fail();
      }
      catch (err) {
        assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief load/unload
////////////////////////////////////////////////////////////////////////////////

    testLoadUnload : function () {
      var c = internal.db._create(cn, { isVolatile : true });
      assertEqual(cn, c.name());
      assertEqual(true, c.properties().isVolatile);
      c.unload();

      internal.wait(4);
      assertEqual(true, c.properties().isVolatile);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief data storage
////////////////////////////////////////////////////////////////////////////////

    testStorage : function () {
      var c = internal.db._create(cn, { isVolatile : true });
      assertEqual(true, c.properties().isVolatile);

      c.save({"test": true});
      assertEqual(1, c.count());
      c.unload();

      internal.wait(4);
      assertEqual(true, c.properties().isVolatile);
      assertEqual(0, c.count());
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief data storage
////////////////////////////////////////////////////////////////////////////////

    testStorageMany : function () {
      var c = internal.db._create(cn, { isVolatile : true, journalSize: 1024 * 1024 });
      assertEqual(true, c.properties().isVolatile);

      for (var i = 0; i < 10000; ++i) {
        c.save({"test": true, "foo" : "bar"});
      }

      assertEqual(10000, c.count());

      c.unload();

      internal.wait(4);
      assertEqual(true, c.properties().isVolatile);
      assertEqual(0, c.count());
      assertEqual([ ], c.toArray());
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief compaction
////////////////////////////////////////////////////////////////////////////////

    testCompaction : function () {
      var c = internal.db._create(cn, { isVolatile : true, journalSize: 1024 * 1024 });
      assertEqual(true, c.properties().isVolatile);

      for (var i = 0; i < 10000; ++i) {
        c.save({"test": "the quick brown fox jumped over the lazy dog", "foo" : "bar"});
      }

      assertEqual(10000, c.count());
      c.truncate();
      c.save({"test": 1});
      assertEqual(1, c.count());
      c.truncate();

      c.unload();

      internal.wait(5);
      assertEqual(0, c.count());
      assertEqual([ ], c.toArray());
    }

  };
}

// -----------------------------------------------------------------------------
// --SECTION--                                                              main
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suites
////////////////////////////////////////////////////////////////////////////////

jsunity.run(CollectionVolatileSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

<|endoftext|>


<commit_before>var styles = {
      cardImage: {
        height: "300px",
        maxHeight: "100%",
        maxWidth: "300px",
        display: "block",
        marginLeft: "auto",
        marginRight: "auto",
        width: "auto",
        minWidth: "0%",
        backgroundColor: "dimgray",
        padding: "14px"
      },
      paper: {
        marginBottom: "50px"
      }
    };

export default styles;<commit_msg>added margin bottom (looks better on small screens)<commit_after>var styles = {
      cardImage: {
        height: "300px",
        maxHeight: "100%",
        maxWidth: "300px",
        display: "block",
        marginLeft: "auto",
        marginRight: "auto",
        width: "auto",
        minWidth: "0%",
        backgroundColor: "dimgray",
        padding: "14px",
        marginBottom: "50px"
      },
      paper: {
        marginBottom: "50px"
      }
    };

export default styles;<|endoftext|>


<commit_before>"use strict";

var $ = require('jquery'),
    _ = require('lodash');

var dom = {
    cancelDrawArea: '.cancel-draw-area',
    clearLocationSearch: '.clear-location-search',
    drawAreaButton: '.draw-area-button',
    controls: {
        standard: '#location-search-well',
        drawArea: '#draw-area-controls',
        customArea: '#custom-area-controls',
        editArea: '#edit-area-controls'
    }
};

var map,
    modes,
    adHocPolygonLayer;

function init(options) {
    map = options.map;
    modes = options.modes;

    $(dom.drawAreaButton).click(modes.activateDrawAreaMode);
    $(dom.cancelDrawArea).click(cancelDrawArea);
    $(dom.clearLocationSearch).click(clearLocationSearch);
}

function cancelDrawArea() {
    clearLocationSearch();
    modes.activateBrowseTreesMode(true);
}

function clearLocationSearch() {
    // Note this may be called from any mode, not just drawAreaMode
    if (adHocPolygonLayer) {
        map.removeLayer(adHocPolygonLayer);
        adHocPolygonLayer = null;
    }
    showControls(dom.controls.standard);
}

function onActivate() {
    showControls(dom.controls.drawArea);
    $(document).on('keydown', onKeyDown);
}

function onKeyDown(e) {
    if (e.keyCode == 27) {  // Escape key
        clearLocationSearch();
    }
}

function onNewPolygon(polygonLayer) {
    adHocPolygonLayer = polygonLayer.addTo(map);
    modes.activateBrowseTreesMode(true);
}

function onDeactivate() {
    showControls(adHocPolygonLayer ? dom.controls.customArea : dom.controls.standard);
    $(document).off('keydown', onKeyDown);
}

function showControls(controls) {
    _.each(dom.controls, function (c) {
        $(c).hide();
    });
    $(controls).show();
}

module.exports = {
    init: init,
    onActivate: onActivate,
    onNewPolygon: onNewPolygon,
    onDeactivate: onDeactivate
};
<commit_msg>Activate browseTreesMode on ESC during polygon drawing<commit_after>"use strict";

var $ = require('jquery'),
    _ = require('lodash');

var dom = {
    cancelDrawArea: '.cancel-draw-area',
    clearLocationSearch: '.clear-location-search',
    drawAreaButton: '.draw-area-button',
    controls: {
        standard: '#location-search-well',
        drawArea: '#draw-area-controls',
        customArea: '#custom-area-controls',
        editArea: '#edit-area-controls'
    }
};

var map,
    modes,
    adHocPolygonLayer;

function init(options) {
    map = options.map;
    modes = options.modes;

    $(dom.drawAreaButton).click(modes.activateDrawAreaMode);
    $(dom.cancelDrawArea).click(cancelDrawArea);
    $(dom.clearLocationSearch).click(clearLocationSearch);
}

function cancelDrawArea() {
    clearLocationSearch();
    modes.activateBrowseTreesMode(true);
}

function clearLocationSearch() {
    // Note this may be called from any mode, not just drawAreaMode
    if (adHocPolygonLayer) {
        map.removeLayer(adHocPolygonLayer);
        adHocPolygonLayer = null;
    }
    showControls(dom.controls.standard);
}

function onActivate() {
    showControls(dom.controls.drawArea);
    $(document).on('keydown', onKeyDown);
}

function onKeyDown(e) {
    if (e.keyCode == 27) {  // Escape key
        cancelDrawArea();
    }
}

function onNewPolygon(polygonLayer) {
    adHocPolygonLayer = polygonLayer.addTo(map);
    modes.activateBrowseTreesMode(true);
}

function onDeactivate() {
    showControls(adHocPolygonLayer ? dom.controls.customArea : dom.controls.standard);
    $(document).off('keydown', onKeyDown);
}

function showControls(controls) {
    _.each(dom.controls, function (c) {
        $(c).hide();
    });
    $(controls).show();
}

module.exports = {
    init: init,
    onActivate: onActivate,
    onNewPolygon: onNewPolygon,
    onDeactivate: onDeactivate
};
<|endoftext|>


<commit_before>import { oppositesToDropdown } from '../utils';
import { symbolApi } from '../../shared';
import config from '../../../common/const';
import { generateLiveApiInstance } from '../../../../common/appId';
import { translate } from '../../../../common/i18n';
import { get as getStorage, set as setStorage } from '../../../../common/utils/storageManager';

let purchaseChoices = [[translate('Click to select'), '']];

export const getPurchaseChoices = () => purchaseChoices;

const filterPurchaseChoices = (contractType, oppositesName) => {
    const { [oppositesName]: tradeTypes } = config.opposites;

    let tmpPurchaseChoices = tradeTypes.filter(k =>
        contractType === 'both' ? true : contractType === Object.keys(k)[0]
    );

    if (!tmpPurchaseChoices.length) {
        tmpPurchaseChoices = tradeTypes;
    }
    return oppositesToDropdown(tmpPurchaseChoices);
};

export const updatePurchaseChoices = (contractType, oppositesName) => {
    purchaseChoices = filterPurchaseChoices(contractType, oppositesName);
    const purchases = Blockly.mainWorkspace
        .getAllBlocks()
        .filter(r => ['purchase', 'payout', 'ask_price'].indexOf(r.type) >= 0);
    Blockly.Events.recordUndo = false;
    purchases.forEach(purchase => {
        const value = purchase.getField('PURCHASE_LIST').getValue();
        Blockly.WidgetDiv.hideIfOwner(purchase.getField('PURCHASE_LIST'));
        if (value === purchaseChoices[0][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        } else if (purchaseChoices.length === 2 && value === purchaseChoices[1][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[1][0]);
        } else {
            purchase.getField('PURCHASE_LIST').setValue(purchaseChoices[0][1]);
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        }
    });
    Blockly.Events.recordUndo = true;
};

export const expectValue = (block, field) => {
    const value = Blockly.JavaScript.valueToCode(block, field, Blockly.JavaScript.ORDER_ATOMIC);
    if (!value) {
        throw Error(translate(`${field} cannot be empty`));
    }
    return value;
};

export const fieldGeneratorMapping = {};

const getActiveSymbols = symbols =>
    Object.keys(symbols).reduce(
        (acc, symbol) =>
            symbolApi.getAllowedCategories(symbol).length ? { ...acc, [symbol]: symbols[symbol] } : { ...acc },
        {}
    );

const getActiveSubMarket = submarkets =>
    Object.keys(submarkets).reduce(
        (acc, submarket) =>
            Object.keys(getActiveSymbols(submarkets[submarket].symbols)).length
                ? { ...acc, [submarket]: submarkets[submarket] }
                : { ...acc },
        {}
    );

const getActiveMarket = markets =>
    Object.keys(markets).reduce(
        (acc, market) =>
            Object.keys(getActiveSubMarket(markets[market].submarkets)).length
                ? { ...acc, [market]: markets[market] }
                : { ...acc },
        {}
    );

fieldGeneratorMapping.MARKET_LIST = () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    return Object.keys(markets).map(e => [markets[e].name, e]);
};

fieldGeneratorMapping.SUBMARKET_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const marketName = block.getFieldValue('MARKET_LIST');
    if (!marketName || marketName === 'Invalid') {
        return [['', 'Invalid']];
    }
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    return Object.keys(submarkets).map(e => [submarkets[e].name, e]);
};

fieldGeneratorMapping.SYMBOL_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const submarketName = block.getFieldValue('SUBMARKET_LIST');
    if (!submarketName || submarketName === 'Invalid') {
        return [['', '']];
    }
    const marketName = block.getFieldValue('MARKET_LIST');
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    const symbols = getActiveSymbols(submarkets[submarketName].symbols);
    return Object.keys(symbols).map(e => [symbols[e].display, symbols[e].symbol]);
};

fieldGeneratorMapping.TRADETYPECAT_LIST = block => () => {
    const symbol = block.getFieldValue('SYMBOL_LIST');
    if (!symbol) {
        return [['', '']];
    }
    const allowedCategories = symbolApi.getAllowedCategories(symbol.toLowerCase());
    return Object.keys(config.conditionsCategoryName)
        .filter(e => allowedCategories.indexOf(e) >= 0)
        .map(e => [config.conditionsCategoryName[e], e]);
};

fieldGeneratorMapping.TRADETYPE_LIST = block => () => {
    const tradeTypeCat = block.getFieldValue('TRADETYPECAT_LIST');
    if (!tradeTypeCat) {
        return [['', '']];
    }
    return config.conditionsCategory[tradeTypeCat].map(e => [
        config.opposites[e.toUpperCase()].map(c => c[Object.keys(c)[0]]).join('/'),
        e,
    ]);
};

export const dependentFieldMapping = {
    MARKET_LIST      : 'SUBMARKET_LIST',
    SUBMARKET_LIST   : 'SYMBOL_LIST',
    SYMBOL_LIST      : 'TRADETYPECAT_LIST',
    TRADETYPECAT_LIST: 'TRADETYPE_LIST',
};

export const getAvailableDurations = (symbol, selectedContractType) => {
    const contractsForStore = JSON.parse(getStorage('contractsForStore') || '[]');
    const defaultDurations = [
        [translate('Ticks'), 't'],
        [translate('Seconds'), 's'],
        [translate('Minutes'), 'm'],
        [translate('Hours'), 'h'],
        [translate('Days'), 'd'],
    ];
    const getContractsForSymbolFromApi = async underlyingSymbol => {
        // Refactor this when reducing WS connections
        let api = generateLiveApiInstance();
        const response = await api.getContractsForSymbol(underlyingSymbol);
        const contractsForSymbol = {};
        if (response.contracts_for) {
            Object.assign(contractsForSymbol, {
                symbol   : underlyingSymbol,
                available: response.contracts_for.available,
                timestamp: Date.now(),
            });
            contractsForStore.push(contractsForSymbol);
            setStorage('contractsForStore', JSON.stringify(contractsForStore));
        }
        api.disconnect();
        api = null;
        return contractsForSymbol;
    };
    const getDurationsForContract = contractsForSymbol => {
        if (!contractsForSymbol) return defaultDurations;

        // Resolve contract_category (e.g. risefall = callput)
        const contractCategory = Object.keys(config.conditionsCategory).find(
            c => c === selectedContractType || config.conditionsCategory[c].includes(selectedContractType)
        );

        // Get contracts based on `contract_category` and `barrier_category`
        const contractsForContractCategory = contractsForSymbol.filter(c => {
            const meetsBarrierConditions = () => {
                const conditions = [];
                Object.keys(config.barrierCategories).some(barrierCategory => {
                    if (config.barrierCategories[barrierCategory].includes(selectedContractType)) {
                        conditions.push(c.barrier_category === barrierCategory);
                    }
                    return conditions.length;
                });
                // If `barrierCategory` for `selectedContractType` not found fallback to all contracts for durations
                return !conditions.includes(false);
            };
            return c.contract_category === contractCategory && meetsBarrierConditions();
        });

        const getDurationIndex = input => defaultDurations.findIndex(d => d[1] === input.replace(/\d+/g, ''));

        // Generate list of available durations from filtered contracts
        const offeredDurations = [];
        contractsForContractCategory.forEach(c => {
            const startIndex = getDurationIndex(c.min_contract_duration);
            const endIndex = getDurationIndex(c.max_contract_duration);
            defaultDurations.slice(startIndex, endIndex + 1).forEach(duration => {
                if (!offeredDurations.includes(duration)) {
                    offeredDurations.push(duration);
                }
            });
        });
        const isIntraday = contractsForContractCategory.every(c => c.expiry_type === 'intraday');
        if (isIntraday) {
            const dayDurationIndex = offeredDurations.findIndex(d => d[1] === 'd');
            if (dayDurationIndex) {
                offeredDurations.splice(dayDurationIndex, 1);
            }
        }
        if (offeredDurations.length) {
            offeredDurations.sort((a, b) => getDurationIndex(a[1]) - getDurationIndex(b[1]));
            return offeredDurations;
        }
        return config.durationTypes[selectedContractType.toUpperCase()];
    };
    // Check if we have local data to get durations from
    const contractsForSymbol = contractsForStore.find(c => c.symbol === symbol);
    if (contractsForSymbol) {
        // If `contracts_for` data is expired, request new data in background but return current cached data
        if (Math.floor((Date.now() - contractsForSymbol.timestamp) / 1000) > 600) {
            getContractsForSymbolFromApi(symbol);
        }
        return Promise.resolve(getDurationsForContract(contractsForSymbol.available));
    }
    return new Promise(resolve => {
        getContractsForSymbolFromApi(symbol).then(contractsForSymbolFromApi => {
            resolve(getDurationsForContract(contractsForSymbolFromApi.available));
        });
    });
};
<commit_msg>Refactor logic for removing day-durations<commit_after>import { oppositesToDropdown } from '../utils';
import { symbolApi } from '../../shared';
import config from '../../../common/const';
import { generateLiveApiInstance } from '../../../../common/appId';
import { translate } from '../../../../common/i18n';
import { get as getStorage, set as setStorage } from '../../../../common/utils/storageManager';

let purchaseChoices = [[translate('Click to select'), '']];

export const getPurchaseChoices = () => purchaseChoices;

const filterPurchaseChoices = (contractType, oppositesName) => {
    const { [oppositesName]: tradeTypes } = config.opposites;

    let tmpPurchaseChoices = tradeTypes.filter(k =>
        contractType === 'both' ? true : contractType === Object.keys(k)[0]
    );

    if (!tmpPurchaseChoices.length) {
        tmpPurchaseChoices = tradeTypes;
    }
    return oppositesToDropdown(tmpPurchaseChoices);
};

export const updatePurchaseChoices = (contractType, oppositesName) => {
    purchaseChoices = filterPurchaseChoices(contractType, oppositesName);
    const purchases = Blockly.mainWorkspace
        .getAllBlocks()
        .filter(r => ['purchase', 'payout', 'ask_price'].indexOf(r.type) >= 0);
    Blockly.Events.recordUndo = false;
    purchases.forEach(purchase => {
        const value = purchase.getField('PURCHASE_LIST').getValue();
        Blockly.WidgetDiv.hideIfOwner(purchase.getField('PURCHASE_LIST'));
        if (value === purchaseChoices[0][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        } else if (purchaseChoices.length === 2 && value === purchaseChoices[1][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[1][0]);
        } else {
            purchase.getField('PURCHASE_LIST').setValue(purchaseChoices[0][1]);
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        }
    });
    Blockly.Events.recordUndo = true;
};

export const expectValue = (block, field) => {
    const value = Blockly.JavaScript.valueToCode(block, field, Blockly.JavaScript.ORDER_ATOMIC);
    if (!value) {
        throw Error(translate(`${field} cannot be empty`));
    }
    return value;
};

export const fieldGeneratorMapping = {};

const getActiveSymbols = symbols =>
    Object.keys(symbols).reduce(
        (acc, symbol) =>
            symbolApi.getAllowedCategories(symbol).length ? { ...acc, [symbol]: symbols[symbol] } : { ...acc },
        {}
    );

const getActiveSubMarket = submarkets =>
    Object.keys(submarkets).reduce(
        (acc, submarket) =>
            Object.keys(getActiveSymbols(submarkets[submarket].symbols)).length
                ? { ...acc, [submarket]: submarkets[submarket] }
                : { ...acc },
        {}
    );

const getActiveMarket = markets =>
    Object.keys(markets).reduce(
        (acc, market) =>
            Object.keys(getActiveSubMarket(markets[market].submarkets)).length
                ? { ...acc, [market]: markets[market] }
                : { ...acc },
        {}
    );

fieldGeneratorMapping.MARKET_LIST = () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    return Object.keys(markets).map(e => [markets[e].name, e]);
};

fieldGeneratorMapping.SUBMARKET_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const marketName = block.getFieldValue('MARKET_LIST');
    if (!marketName || marketName === 'Invalid') {
        return [['', 'Invalid']];
    }
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    return Object.keys(submarkets).map(e => [submarkets[e].name, e]);
};

fieldGeneratorMapping.SYMBOL_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const submarketName = block.getFieldValue('SUBMARKET_LIST');
    if (!submarketName || submarketName === 'Invalid') {
        return [['', '']];
    }
    const marketName = block.getFieldValue('MARKET_LIST');
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    const symbols = getActiveSymbols(submarkets[submarketName].symbols);
    return Object.keys(symbols).map(e => [symbols[e].display, symbols[e].symbol]);
};

fieldGeneratorMapping.TRADETYPECAT_LIST = block => () => {
    const symbol = block.getFieldValue('SYMBOL_LIST');
    if (!symbol) {
        return [['', '']];
    }
    const allowedCategories = symbolApi.getAllowedCategories(symbol.toLowerCase());
    return Object.keys(config.conditionsCategoryName)
        .filter(e => allowedCategories.indexOf(e) >= 0)
        .map(e => [config.conditionsCategoryName[e], e]);
};

fieldGeneratorMapping.TRADETYPE_LIST = block => () => {
    const tradeTypeCat = block.getFieldValue('TRADETYPECAT_LIST');
    if (!tradeTypeCat) {
        return [['', '']];
    }
    return config.conditionsCategory[tradeTypeCat].map(e => [
        config.opposites[e.toUpperCase()].map(c => c[Object.keys(c)[0]]).join('/'),
        e,
    ]);
};

export const dependentFieldMapping = {
    MARKET_LIST      : 'SUBMARKET_LIST',
    SUBMARKET_LIST   : 'SYMBOL_LIST',
    SYMBOL_LIST      : 'TRADETYPECAT_LIST',
    TRADETYPECAT_LIST: 'TRADETYPE_LIST',
};

export const getAvailableDurations = (symbol, selectedContractType) => {
    const contractsForStore = JSON.parse(getStorage('contractsForStore') || '[]');
    const defaultDurations = [
        [translate('Ticks'), 't'],
        [translate('Seconds'), 's'],
        [translate('Minutes'), 'm'],
        [translate('Hours'), 'h'],
        [translate('Days'), 'd'],
    ];
    const getContractsForSymbolFromApi = async underlyingSymbol => {
        // Refactor this when reducing WS connections
        let api = generateLiveApiInstance();
        const response = await api.getContractsForSymbol(underlyingSymbol);
        const contractsForSymbol = {};
        if (response.contracts_for) {
            Object.assign(contractsForSymbol, {
                symbol   : underlyingSymbol,
                available: response.contracts_for.available,
                timestamp: Date.now(),
            });
            contractsForStore.push(contractsForSymbol);
            setStorage('contractsForStore', JSON.stringify(contractsForStore));
        }
        api.disconnect();
        api = null;
        return contractsForSymbol;
    };
    const getDurationsForContract = contractsForSymbol => {
        if (!contractsForSymbol) return defaultDurations;

        // Resolve contract_category (e.g. risefall = callput)
        const contractCategory = Object.keys(config.conditionsCategory).find(
            c => c === selectedContractType || config.conditionsCategory[c].includes(selectedContractType)
        );

        // Get contracts based on `contract_category` and `barrier_category`
        const contractsForContractCategory = contractsForSymbol.filter(c => {
            const meetsBarrierConditions = () => {
                const conditions = [];
                Object.keys(config.barrierCategories).some(barrierCategory => {
                    if (config.barrierCategories[barrierCategory].includes(selectedContractType)) {
                        conditions.push(c.barrier_category === barrierCategory);
                    }
                    return conditions.length;
                });
                // If `barrierCategory` for `selectedContractType` not found fallback to all contracts for durations
                return !conditions.includes(false);
            };
            return c.contract_category === contractCategory && meetsBarrierConditions();
        });

        const getDurationIndex = input => defaultDurations.findIndex(d => d[1] === input.replace(/\d+/g, ''));

        // Generate list of available durations from filtered contracts
        const offeredDurations = [];
        contractsForContractCategory.forEach(c => {
            const startIndex = getDurationIndex(c.min_contract_duration);
            const endIndex = getDurationIndex(c.max_contract_duration);
            defaultDurations.slice(startIndex, endIndex + 1).forEach(duration => {
                if (!offeredDurations.includes(duration)) {
                    offeredDurations.push(duration);
                }
            });
        });
        if (contractsForContractCategory.every(c => c.expiry_type === 'intraday')) {
            const dayDurationIndex = offeredDurations.findIndex(d => d[1] === 'd');
            if (dayDurationIndex) {
                offeredDurations.splice(dayDurationIndex, 1);
            }
        }
        if (offeredDurations.length) {
            offeredDurations.sort((a, b) => getDurationIndex(a[1]) - getDurationIndex(b[1]));
            return offeredDurations;
        }
        return config.durationTypes[selectedContractType.toUpperCase()];
    };
    // Check if we have local data to get durations from
    const contractsForSymbol = contractsForStore.find(c => c.symbol === symbol);
    if (contractsForSymbol) {
        // If `contracts_for` data is expired, request new data in background but return current cached data
        if (Math.floor((Date.now() - contractsForSymbol.timestamp) / 1000) > 600) {
            getContractsForSymbolFromApi(symbol);
        }
        return Promise.resolve(getDurationsForContract(contractsForSymbol.available));
    }
    return new Promise(resolve => {
        getContractsForSymbolFromApi(symbol).then(contractsForSymbolFromApi => {
            resolve(getDurationsForContract(contractsForSymbolFromApi.available));
        });
    });
};
<|endoftext|>


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

/**
 * Registers the license entry for a media file by adding it to global.phet.chipper.licenseEntries.
 * Also verifies that the file's license is "acceptable" (as defined in isAcceptableLicenseEntry below).
 * Intended to be called by media plugins at the time that media files are loaded.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function() {
  'use strict';

  /**
   * Determines whether a license entry is compatible with a brand's licensing policies.
   * @param {*} entry see getLicenseEntry.js
   * @param {string} brand
   * @returns {boolean}
   */
  function isCompatibleLicenseEntry( entry, brand ) {
    if ( brand === 'phet' || brand === 'phet-io' ) {
      // PhET-specific brands have these licensing policies
      return entry.projectURL === 'http://phet.colorado.edu' ||
             entry.license === 'Public Domain' ||
             entry.license === 'NASA';
    }
    else {
      // non-PhET brands have no licensing policies, so all entries are compatible
      return true;
    }
  }

  /**
   * Determines whether a license entry is "acceptable".  A license entry is acceptable
   * if the entry is compatible with the licensing polices for the specified brand,
   * or if the entry has an "exception" field explaining why the licensing policies can be ignored.
   *
   * @param {string} name name of the resource whose license entry is being checked
   * @param {*} entry see getLicenseEntry.js
   * @param {string} brand
   * @returns {boolean}
   */
  function isAcceptableLicenseEntry( name, entry, brand ) {
    var acceptable = isCompatibleLicenseEntry( entry, brand ) || !!entry.exception;
    if ( acceptable && !!entry.exception ) {
      var warningMessage = 'license exception for ' + name + ': ' + entry.exception;
      if ( global.phet.chipper.grunt ) {
        // use grunt.log if it's available
        global.phet.chipper.grunt.log.warn( warningMessage );
      }
      else {
        console.log( 'WARNING: ' + warningMessage );
      }
    }
    return acceptable;
  }

  /**
   * Registers the license entry for a media file.
   * Intended to be called by media plugins.
   *
   * @param {string} name - the symbolic media filename as supplied to the plugin, e.g. JOHN_TRAVOLTAGE/arm.png
   * @param {*} licenseEntry - license entry for the media file, documented in getLicenseEntry.js
   * @param {string} brand
   * @param {string} mediaType - the type of media and name of the media subdirectory ('audio'|'image'|'mipmap'|...)
   * @param {function} onload - a function that conforms to the requirejs onload API
   */
  function registerLicenseEntry( name, licenseEntry, brand, mediaType, onload ) {
    if ( isAcceptableLicenseEntry( name, licenseEntry, brand ) ) {
      global.phet.chipper.licenseEntries[ mediaType ] = global.phet.chipper.licenseEntries[ mediaType ] || {};
      global.phet.chipper.licenseEntries[ mediaType ][ name ] = licenseEntry;
      onload( null );
    }
    else {
      onload.error( new Error( 'unacceptable license entry for ' + name ) );
    }
  }

  return registerLicenseEntry;
} );<commit_msg>Added docs, see #229<commit_after>//  Copyright 2002-2014, University of Colorado Boulder

/**
 * Registers the license entry for a media file by adding it to global.phet.chipper.licenseEntries.
 * Also verifies that the file's license is "acceptable" (as defined in isAcceptableLicenseEntry below).
 * Intended to be called by media plugins at the time that media files are loaded.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function() {
  'use strict';

  /**
   * Determines whether a license entry is compatible with a brand's licensing policies.
   * @param {object} entry - see getLicenseEntry.js
   * @param {string} brand - the string for the selected brand, such as 'phet'
   * @returns {boolean}
   */
  function isCompatibleLicenseEntry( entry, brand ) {
    if ( brand === 'phet' || brand === 'phet-io' ) {
      // PhET-specific brands have these licensing policies
      return entry.projectURL === 'http://phet.colorado.edu' ||
             entry.license === 'Public Domain' ||
             entry.license === 'NASA';
    }
    else {
      // non-PhET brands have no licensing policies, so all entries are compatible
      return true;
    }
  }

  /**
   * Determines whether a license entry is "acceptable".  A license entry is acceptable
   * if the entry is compatible with the licensing polices for the specified brand,
   * or if the entry has an "exception" field explaining why the licensing policies can be ignored.
   *
   * @param {string} name - name of the resource whose license entry is being checked
   * @param {object} entry - see getLicenseEntry.js
   * @param {string} brand - the string for the selected brand, such as 'phet'
   * @returns {boolean}
   */
  function isAcceptableLicenseEntry( name, entry, brand ) {
    var acceptable = isCompatibleLicenseEntry( entry, brand ) || !!entry.exception;
    if ( acceptable && !!entry.exception ) {
      var warningMessage = 'license exception for ' + name + ': ' + entry.exception;
      if ( global.phet.chipper.grunt ) {
        // use grunt.log if it's available
        global.phet.chipper.grunt.log.warn( warningMessage );
      }
      else {
        console.log( 'WARNING: ' + warningMessage );
      }
    }
    return acceptable;
  }

  /**
   * Registers the license entry for a media file.
   * Intended to be called by media plugins.
   *
   * @param {string} name - the symbolic media filename as supplied to the plugin, e.g. JOHN_TRAVOLTAGE/arm.png
   * @param {object} licenseEntry - license entry for the media file, documented in getLicenseEntry.js
   * @param {string} brand - the string for the selected brand, such as 'phet'
   * @param {string} mediaType - the type of media and name of the media subdirectory ('audio'|'image'|'mipmap'|...)
   * @param {function} onload - a function that conforms to the requirejs onload API
   */
  function registerLicenseEntry( name, licenseEntry, brand, mediaType, onload ) {
    if ( isAcceptableLicenseEntry( name, licenseEntry, brand ) ) {
      global.phet.chipper.licenseEntries[ mediaType ] = global.phet.chipper.licenseEntries[ mediaType ] || {};
      global.phet.chipper.licenseEntries[ mediaType ][ name ] = licenseEntry;
      onload( null );
    }
    else {
      onload.error( new Error( 'unacceptable license entry for ' + name ) );
    }
  }

  return registerLicenseEntry;
} );<|endoftext|>


<commit_before>var parse = require('dox').parseComments;
var extractModuleMeta = require('./utils/extract-module-meta');

/**
 * @param {string} content
 * @param {Object} context
 * @param {Object} context.loader Loader context
 * @param {Object} [context.plugin] Plugin instance
 * @returns {{body: Array}}
 */
module.exports = function(content, context) {
  var parsed = parse(content);
  var meta = extractModuleMeta(parsed);

  return {
    meta: meta,
    content: parsed
  };
};<commit_msg>Update default extractor to extract examples<commit_after>var parse = require('dox').parseComments;
var extractModuleMeta = require('./utils/extract-module-meta');
var examplesParser = require('./utils/examples-parser');

/**
 * @param {string} source
 * @param {Object} context
 * @param {Object} context.loader Loader context
 * @param {Object} [context.plugin] Plugin instance
 * @returns {{body: Array}}
 */
module.exports = function(source, context) {
  var content = parse(source);
  var meta = extractModuleMeta(content);

  content
    .filter(function (record) { return typeof record.tags !== 'undefined' })
    .forEach(function (record) {
      record.tags
        .filter(function (tag) {
          return tag.type === 'example'
        })
        .forEach(function (tag) {
          var examples = examplesParser(tag.string);
          if (examples.length > 0)
            record.examples = examples;
        })
    });

  return {
    meta: meta,
    content: content
  };
};<|endoftext|>


<commit_before>import { oppositesToDropdown } from '../utils';
import { symbolApi } from '../../shared';
import config from '../../../common/const';
import { generateLiveApiInstance } from '../../../../common/appId';
import { translate } from '../../../../common/i18n';
import { get as getStorage, set as setStorage, getTokenList } from '../../../../common/utils/storageManager';

let purchaseChoices = [[translate('Click to select'), '']];

export const getPurchaseChoices = () => purchaseChoices;

const filterPurchaseChoices = (contractType, oppositesName) => {
    const { [oppositesName]: tradeTypes } = config.opposites;

    let tmpPurchaseChoices = tradeTypes.filter(k =>
        contractType === 'both' ? true : contractType === Object.keys(k)[0]
    );

    if (!tmpPurchaseChoices.length) {
        tmpPurchaseChoices = tradeTypes;
    }
    return oppositesToDropdown(tmpPurchaseChoices);
};

export const updatePurchaseChoices = (contractType, oppositesName) => {
    purchaseChoices = filterPurchaseChoices(contractType, oppositesName);
    const purchases = Blockly.mainWorkspace
        .getAllBlocks()
        .filter(r => ['purchase', 'payout', 'ask_price'].indexOf(r.type) >= 0);
    Blockly.Events.recordUndo = false;
    purchases.forEach(purchase => {
        const value = purchase.getField('PURCHASE_LIST').getValue();
        Blockly.WidgetDiv.hideIfOwner(purchase.getField('PURCHASE_LIST'));
        if (value === purchaseChoices[0][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        } else if (purchaseChoices.length === 2 && value === purchaseChoices[1][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[1][0]);
        } else {
            purchase.getField('PURCHASE_LIST').setValue(purchaseChoices[0][1]);
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        }
    });
    Blockly.Events.recordUndo = true;
};

export const expectValue = (block, field) => {
    const value = Blockly.JavaScript.valueToCode(block, field, Blockly.JavaScript.ORDER_ATOMIC);
    if (!value) {
        throw Error(translate(`${field} cannot be empty`));
    }
    return value;
};

export const fieldGeneratorMapping = {};

const getActiveSymbols = symbols =>
    Object.keys(symbols).reduce(
        (acc, symbol) =>
            symbolApi.getAllowedCategories(symbol).length ? { ...acc, [symbol]: symbols[symbol] } : { ...acc },
        {}
    );

const getActiveSubMarket = submarkets =>
    Object.keys(submarkets).reduce(
        (acc, submarket) =>
            Object.keys(getActiveSymbols(submarkets[submarket].symbols)).length
                ? { ...acc, [submarket]: submarkets[submarket] }
                : { ...acc },
        {}
    );

const getActiveMarket = markets =>
    Object.keys(markets).reduce(
        (acc, market) =>
            Object.keys(getActiveSubMarket(markets[market].submarkets)).length
                ? { ...acc, [market]: markets[market] }
                : { ...acc },
        {}
    );

fieldGeneratorMapping.MARKET_LIST = () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    return Object.keys(markets).map(e => [markets[e].name, e]);
};

fieldGeneratorMapping.SUBMARKET_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const marketName = block.getFieldValue('MARKET_LIST');
    if (!marketName || marketName === 'Invalid') {
        return [['', 'Invalid']];
    }
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    return Object.keys(submarkets).map(e => [submarkets[e].name, e]);
};

fieldGeneratorMapping.SYMBOL_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const submarketName = block.getFieldValue('SUBMARKET_LIST');
    if (!submarketName || submarketName === 'Invalid') {
        return [['', '']];
    }
    const marketName = block.getFieldValue('MARKET_LIST');
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    const symbols = getActiveSymbols(submarkets[submarketName].symbols);
    return Object.keys(symbols).map(e => [symbols[e].display, symbols[e].symbol]);
};

fieldGeneratorMapping.TRADETYPECAT_LIST = block => () => {
    const symbol = block.getFieldValue('SYMBOL_LIST');
    if (!symbol) {
        return [['', '']];
    }
    const allowedCategories = symbolApi.getAllowedCategories(symbol.toLowerCase());
    return Object.keys(config.conditionsCategoryName)
        .filter(e => allowedCategories.indexOf(e) >= 0)
        .map(e => [config.conditionsCategoryName[e], e]);
};

fieldGeneratorMapping.TRADETYPE_LIST = block => () => {
    const tradeTypeCat = block.getFieldValue('TRADETYPECAT_LIST');
    if (!tradeTypeCat) {
        return [['', '']];
    }
    return config.conditionsCategory[tradeTypeCat].map(e => [
        config.opposites[e.toUpperCase()].map(c => c[Object.keys(c)[0]]).join('/'),
        e,
    ]);
};

export const dependentFieldMapping = {
    MARKET_LIST      : 'SUBMARKET_LIST',
    SUBMARKET_LIST   : 'SYMBOL_LIST',
    SYMBOL_LIST      : 'TRADETYPECAT_LIST',
    TRADETYPECAT_LIST: 'TRADETYPE_LIST',
};

export const getAvailableDurations = (symbol, selectedContractType) => {
    const contractsForStore = JSON.parse(getStorage('contractsForStore') || '[]');
    const tokenList = getTokenList();
    const defaultDurations = [
        [translate('Ticks'), 't'],
        [translate('Seconds'), 's'],
        [translate('Minutes'), 'm'],
        [translate('Hours'), 'h'],
        [translate('Days'), 'd'],
    ];
    const getContractsForSymbolFromApi = async underlyingSymbol => {
        // Refactor this when reducing WS connections
        let api = generateLiveApiInstance();

        // Try to authorize for accurate contracts response
        if (tokenList.length) {
            await api.authorize(tokenList[0].token);
        }

        const response = await api.getContractsForSymbol(underlyingSymbol);
        const contractsForSymbol = {};
        if (response.contracts_for) {
            Object.assign(contractsForSymbol, {
                symbol   : underlyingSymbol,
                available: response.contracts_for.available,
                timestamp: Date.now(),
            });
            if (tokenList.length) {
                Object.assign(contractsForSymbol, {
                    accountName: tokenList[0].accountName,
                });
            }
            // Avoid duplicate symbols in contractsForStore
            contractsForStore
                .filter(c => c.symbol === underlyingSymbol)
                .forEach(() =>
                    contractsForStore.splice(contractsForStore.findIndex(c => c.symbol === underlyingSymbol), 1)
                );
            contractsForStore.push(contractsForSymbol);
            setStorage('contractsForStore', JSON.stringify(contractsForStore));
        }
        api.disconnect();
        api = null;
        return contractsForSymbol;
    };
    const getDurationsForContract = contractsForSymbol => {
        if (!contractsForSymbol) return defaultDurations;

        // Resolve contract_category (e.g. risefall = callput)
        const contractCategory = Object.keys(config.conditionsCategory).find(
            c => c === selectedContractType || config.conditionsCategory[c].includes(selectedContractType)
        );

        // Get contracts based on `contract_category` and `barrier_category`
        const contractsForContractCategory = contractsForSymbol.filter(c => {
            const meetsBarrierConditions = () => {
                const conditions = [];
                Object.keys(config.barrierCategories).some(barrierCategory => {
                    if (config.barrierCategories[barrierCategory].includes(selectedContractType)) {
                        conditions.push(c.barrier_category === barrierCategory);
                    }
                    return conditions.length;
                });
                // If `barrierCategory` for `selectedContractType` not found fallback to all contracts for durations
                return !conditions.includes(false);
            };
            // We don't offer forward-starting contracts in Binary Bot, remove these
            if (c.start_type === 'forward') {
                return false;
            }
            return c.contract_category === contractCategory && meetsBarrierConditions();
        });

        const getDurationIndex = input => defaultDurations.findIndex(d => d[1] === input.replace(/\d+/g, ''));

        // Generate list of available durations from filtered contracts
        const offeredDurations = [];
        contractsForContractCategory.forEach(c => {
            const startIndex = getDurationIndex(c.min_contract_duration);
            const endIndex = getDurationIndex(c.max_contract_duration);
            defaultDurations.slice(startIndex, endIndex + 1).forEach(duration => {
                if (!offeredDurations.includes(duration)) {
                    offeredDurations.push(duration);
                }
            });
        });
        // If only intraday contracts are available, remove day-durations
        if (contractsForContractCategory.every(c => c.expiry_type === 'intraday')) {
            const dayDurationIndex = offeredDurations.findIndex(d => d[1] === 'd');
            if (dayDurationIndex) {
                offeredDurations.splice(dayDurationIndex, 1);
            }
        }
        offeredDurations.sort((a, b) => getDurationIndex(a[1]) - getDurationIndex(b[1]));
        return offeredDurations;
    };
    // Check if we have local data to get durations from
    const contractsForSymbol = contractsForStore.find(c => c.symbol === symbol);
    if (contractsForSymbol) {
        const isExpiredData = () => Math.floor((Date.now() - contractsForSymbol.timestamp) / 1000) > 600;
        const isDifferentAccount = () =>
            tokenList.length && contractsForSymbol.accountName !== tokenList[0].accountName;
        if (isExpiredData || isDifferentAccount) {
            getContractsForSymbolFromApi(symbol);
        }
        // Returns cached data, but if isExpiredData or isDifferentAccount new data is req in background
        return Promise.resolve(getDurationsForContract(contractsForSymbol.available));
    }
    return new Promise(resolve => {
        getContractsForSymbolFromApi(symbol).then(contractsForSymbolFromApi => {
            resolve(getDurationsForContract(contractsForSymbolFromApi.available));
        });
    });
};
<commit_msg>Only serve cached data when expired, serve fresh for diff accountName<commit_after>import { oppositesToDropdown } from '../utils';
import { symbolApi } from '../../shared';
import config from '../../../common/const';
import { generateLiveApiInstance } from '../../../../common/appId';
import { translate } from '../../../../common/i18n';
import { get as getStorage, set as setStorage, getTokenList } from '../../../../common/utils/storageManager';

let purchaseChoices = [[translate('Click to select'), '']];

export const getPurchaseChoices = () => purchaseChoices;

const filterPurchaseChoices = (contractType, oppositesName) => {
    const { [oppositesName]: tradeTypes } = config.opposites;

    let tmpPurchaseChoices = tradeTypes.filter(k =>
        contractType === 'both' ? true : contractType === Object.keys(k)[0]
    );

    if (!tmpPurchaseChoices.length) {
        tmpPurchaseChoices = tradeTypes;
    }
    return oppositesToDropdown(tmpPurchaseChoices);
};

export const updatePurchaseChoices = (contractType, oppositesName) => {
    purchaseChoices = filterPurchaseChoices(contractType, oppositesName);
    const purchases = Blockly.mainWorkspace
        .getAllBlocks()
        .filter(r => ['purchase', 'payout', 'ask_price'].indexOf(r.type) >= 0);
    Blockly.Events.recordUndo = false;
    purchases.forEach(purchase => {
        const value = purchase.getField('PURCHASE_LIST').getValue();
        Blockly.WidgetDiv.hideIfOwner(purchase.getField('PURCHASE_LIST'));
        if (value === purchaseChoices[0][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        } else if (purchaseChoices.length === 2 && value === purchaseChoices[1][1]) {
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[1][0]);
        } else {
            purchase.getField('PURCHASE_LIST').setValue(purchaseChoices[0][1]);
            purchase.getField('PURCHASE_LIST').setText(purchaseChoices[0][0]);
        }
    });
    Blockly.Events.recordUndo = true;
};

export const expectValue = (block, field) => {
    const value = Blockly.JavaScript.valueToCode(block, field, Blockly.JavaScript.ORDER_ATOMIC);
    if (!value) {
        throw Error(translate(`${field} cannot be empty`));
    }
    return value;
};

export const fieldGeneratorMapping = {};

const getActiveSymbols = symbols =>
    Object.keys(symbols).reduce(
        (acc, symbol) =>
            symbolApi.getAllowedCategories(symbol).length ? { ...acc, [symbol]: symbols[symbol] } : { ...acc },
        {}
    );

const getActiveSubMarket = submarkets =>
    Object.keys(submarkets).reduce(
        (acc, submarket) =>
            Object.keys(getActiveSymbols(submarkets[submarket].symbols)).length
                ? { ...acc, [submarket]: submarkets[submarket] }
                : { ...acc },
        {}
    );

const getActiveMarket = markets =>
    Object.keys(markets).reduce(
        (acc, market) =>
            Object.keys(getActiveSubMarket(markets[market].submarkets)).length
                ? { ...acc, [market]: markets[market] }
                : { ...acc },
        {}
    );

fieldGeneratorMapping.MARKET_LIST = () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    return Object.keys(markets).map(e => [markets[e].name, e]);
};

fieldGeneratorMapping.SUBMARKET_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const marketName = block.getFieldValue('MARKET_LIST');
    if (!marketName || marketName === 'Invalid') {
        return [['', 'Invalid']];
    }
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    return Object.keys(submarkets).map(e => [submarkets[e].name, e]);
};

fieldGeneratorMapping.SYMBOL_LIST = block => () => {
    const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets());
    const submarketName = block.getFieldValue('SUBMARKET_LIST');
    if (!submarketName || submarketName === 'Invalid') {
        return [['', '']];
    }
    const marketName = block.getFieldValue('MARKET_LIST');
    const submarkets = getActiveSubMarket(markets[marketName].submarkets);
    const symbols = getActiveSymbols(submarkets[submarketName].symbols);
    return Object.keys(symbols).map(e => [symbols[e].display, symbols[e].symbol]);
};

fieldGeneratorMapping.TRADETYPECAT_LIST = block => () => {
    const symbol = block.getFieldValue('SYMBOL_LIST');
    if (!symbol) {
        return [['', '']];
    }
    const allowedCategories = symbolApi.getAllowedCategories(symbol.toLowerCase());
    return Object.keys(config.conditionsCategoryName)
        .filter(e => allowedCategories.indexOf(e) >= 0)
        .map(e => [config.conditionsCategoryName[e], e]);
};

fieldGeneratorMapping.TRADETYPE_LIST = block => () => {
    const tradeTypeCat = block.getFieldValue('TRADETYPECAT_LIST');
    if (!tradeTypeCat) {
        return [['', '']];
    }
    return config.conditionsCategory[tradeTypeCat].map(e => [
        config.opposites[e.toUpperCase()].map(c => c[Object.keys(c)[0]]).join('/'),
        e,
    ]);
};

export const dependentFieldMapping = {
    MARKET_LIST      : 'SUBMARKET_LIST',
    SUBMARKET_LIST   : 'SYMBOL_LIST',
    SYMBOL_LIST      : 'TRADETYPECAT_LIST',
    TRADETYPECAT_LIST: 'TRADETYPE_LIST',
};

export const getAvailableDurations = (symbol, selectedContractType) => {
    const contractsForStore = JSON.parse(getStorage('contractsForStore') || '[]');
    const tokenList = getTokenList();
    const defaultDurations = [
        [translate('Ticks'), 't'],
        [translate('Seconds'), 's'],
        [translate('Minutes'), 'm'],
        [translate('Hours'), 'h'],
        [translate('Days'), 'd'],
    ];
    const getContractsForSymbolFromApi = async underlyingSymbol => {
        // Refactor this when reducing WS connections
        let api = generateLiveApiInstance();

        // Try to authorize for accurate contracts response
        if (tokenList.length) {
            await api.authorize(tokenList[0].token);
        }

        const response = await api.getContractsForSymbol(underlyingSymbol);
        const contractsForSymbol = {};
        if (response.contracts_for) {
            Object.assign(contractsForSymbol, {
                symbol   : underlyingSymbol,
                available: response.contracts_for.available,
                timestamp: Date.now(),
            });
            if (tokenList.length) {
                Object.assign(contractsForSymbol, {
                    accountName: tokenList[0].accountName,
                });
            }
            // Avoid duplicate symbols in contractsForStore
            contractsForStore
                .filter(c => c.symbol === underlyingSymbol)
                .forEach(() =>
                    contractsForStore.splice(contractsForStore.findIndex(c => c.symbol === underlyingSymbol), 1)
                );
            contractsForStore.push(contractsForSymbol);
            setStorage('contractsForStore', JSON.stringify(contractsForStore));
        }
        api.disconnect();
        api = null;
        return contractsForSymbol;
    };
    const getDurationsForContract = contractsForSymbol => {
        if (!contractsForSymbol) return defaultDurations;

        // Resolve contract_category (e.g. risefall = callput)
        const contractCategory = Object.keys(config.conditionsCategory).find(
            c => c === selectedContractType || config.conditionsCategory[c].includes(selectedContractType)
        );

        // Get contracts based on `contract_category` and `barrier_category`
        const contractsForContractCategory = contractsForSymbol.filter(c => {
            const meetsBarrierConditions = () => {
                const conditions = [];
                Object.keys(config.barrierCategories).some(barrierCategory => {
                    if (config.barrierCategories[barrierCategory].includes(selectedContractType)) {
                        conditions.push(c.barrier_category === barrierCategory);
                    }
                    return conditions.length;
                });
                // If `barrierCategory` for `selectedContractType` not found fallback to all contracts for durations
                return !conditions.includes(false);
            };
            // We don't offer forward-starting contracts in Binary Bot, remove these
            if (c.start_type === 'forward') {
                return false;
            }
            return c.contract_category === contractCategory && meetsBarrierConditions();
        });

        const getDurationIndex = input => defaultDurations.findIndex(d => d[1] === input.replace(/\d+/g, ''));

        // Generate list of available durations from filtered contracts
        const offeredDurations = [];
        contractsForContractCategory.forEach(c => {
            const startIndex = getDurationIndex(c.min_contract_duration);
            const endIndex = getDurationIndex(c.max_contract_duration);
            defaultDurations.slice(startIndex, endIndex + 1).forEach(duration => {
                if (!offeredDurations.includes(duration)) {
                    offeredDurations.push(duration);
                }
            });
        });
        // If only intraday contracts are available, remove day-durations
        if (contractsForContractCategory.every(c => c.expiry_type === 'intraday')) {
            const dayDurationIndex = offeredDurations.findIndex(d => d[1] === 'd');
            if (dayDurationIndex) {
                offeredDurations.splice(dayDurationIndex, 1);
            }
        }
        offeredDurations.sort((a, b) => getDurationIndex(a[1]) - getDurationIndex(b[1]));
        return offeredDurations;
    };

    const getFreshContractsFor = () => new Promise(resolve => {
        getContractsForSymbolFromApi(symbol).then(contractsForSymbolFromApi => {
            resolve(getDurationsForContract(contractsForSymbolFromApi.available));
        });
    });

    // Check if we have local data to get durations from
    const contractsForSymbol = contractsForStore.find(c => c.symbol === symbol);
    if (contractsForSymbol) {
        const isDifferentAccount = () =>
            tokenList.length && contractsForSymbol.accountName !== tokenList[0].accountName;
        const isExpiredData = () => Math.floor((Date.now() - contractsForSymbol.timestamp) / 1000) > 600;
        if (isDifferentAccount()) {
            return getFreshContractsFor();
        } else if (isExpiredData()) {
            // Return cached data, update cached data in background
            getContractsForSymbolFromApi(symbol);
        }
        return Promise.resolve(getDurationsForContract(contractsForSymbol.available));
    }
    return getFreshContractsFor();
};
<|endoftext|>


<commit_before>// Copyright 2016, University of Colorado Boulder

/**
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var assertInstanceOfTypes = require( 'PHET_IO/assertions/assertInstanceOfTypes' );
  var phetioInherit = require( 'PHET_IO/phetioInherit' );
  var phetioNamespace = require( 'PHET_IO/phetioNamespace' );
  var TNode = require( 'PHET_IO/types/scenery/nodes/TNode' );
  var toEventOnEmit = require( 'PHET_IO/events/toEventOnEmit' );

  /**
   * Wrapper type for phet/sun's PushButton class.
   * @param navBarScreenButton
   * @param phetioID
   * @constructor
   */
  function TNavigationBarScreenButton( navBarScreenButton, phetioID ) {
    TNode.call( this, navBarScreenButton, phetioID );

    assertInstanceOfTypes( navBarScreenButton, [
      phet.joist.NavigationBarScreenButton
    ] );

    assert && assert( navBarScreenButton.buttonModel.startedCallbacksForFiredEmitter, 'button models should use emitters' );
    toEventOnEmit( navBarScreenButton.buttonModel.startedCallbacksForFiredEmitter, navBarScreenButton.buttonModel.endedCallbacksForFiredEmitter, 'user', phetioID, TNavigationBarScreenButton, 'fired' );
  }

  phetioInherit( TNode, 'TNavigationBarScreenButton', TNavigationBarScreenButton, {}, {
    documentation: 'A pressable button in the simulation',
    events: [ 'fired' ]
  } );

  phetioNamespace.register( 'TNavigationBarScreenButton', TNavigationBarScreenButton );

  return TNavigationBarScreenButton;
} );<commit_msg>Updated docs, see https://github.com/phetsims/phet-io/issues/1026<commit_after>// Copyright 2016, University of Colorado Boulder

/**
 * Wrapper type for NavigationBarScreenButton
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var assertInstanceOfTypes = require( 'PHET_IO/assertions/assertInstanceOfTypes' );
  var phetioInherit = require( 'PHET_IO/phetioInherit' );
  var phetioNamespace = require( 'PHET_IO/phetioNamespace' );
  var TNode = require( 'PHET_IO/types/scenery/nodes/TNode' );
  var toEventOnEmit = require( 'PHET_IO/events/toEventOnEmit' );

  /**
   * @param {NavigationBarScreenButton} navigationBarScreenButton
   * @param {string} phetioID
   * @constructor
   */
  function TNavigationBarScreenButton( navigationBarScreenButton, phetioID ) {
    TNode.call( this, navigationBarScreenButton, phetioID );

    assertInstanceOfTypes( navigationBarScreenButton, [ phet.joist.NavigationBarScreenButton ] );

    assert && assert( navigationBarScreenButton.buttonModel.startedCallbacksForFiredEmitter, 'button models should use emitters' );

    // Send a message on the data stream when the button is pressed.
    toEventOnEmit(
      navigationBarScreenButton.buttonModel.startedCallbacksForFiredEmitter,
      navigationBarScreenButton.buttonModel.endedCallbacksForFiredEmitter,
      'user',
      phetioID,
      TNavigationBarScreenButton,
      'fired'
    );
  }

  phetioInherit( TNode, 'TNavigationBarScreenButton', TNavigationBarScreenButton, {}, {
    documentation: 'A pressable button in the simulation\'s navigation bar',
    events: [ 'fired' ]
  } );

  phetioNamespace.register( 'TNavigationBarScreenButton', TNavigationBarScreenButton );

  return TNavigationBarScreenButton;
} );<|endoftext|>