<commit_before>const crypto = require('crypto');
const TypeCheck = require('js-typecheck');
const ValidationFail = require('../../Error/ValidationFail');
const Input = require('../../Input');
const Hex = require('./Hex');

/**
 * Hash input.
 *
 * ```javascript
 * const input = Input.create('myInput: hash', {size: 64});
 * input.setValue('b542f74248124c32');
 * ```
 *
 *
Property Summary

 *
 * Property Name | Description | Defined by Default | Default Value
 * --- | --- | :---: | :---:
 * size | size of the hash in bits. This property needs to be defined at constructor \
 *  time | ::true:: | ::none::
 *
 * All properties including the inherited ones can be listed via
 * {@link registeredPropertyNames}
 *
 * @see https://en.wikipedia.org/wiki/Universally_unique_identifier
 */
class Hash extends Hex{

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

    // making sure 'size' property has been defined
    if (TypeCheck.isNone(this.property('size'))){
      throw new Error(`Can't create a hash input (${this.name()}) without the 'size' property!`);
    }

    // locking the 'size' property
    this.lockProperty('size');
  }

  /**
   * Generates a new random hash and assigns it to the value of the input. This method
   * is not supported by vector inputs.
   */
  setRandom(){
    if (this.isVector()){
      throw new Error('Not supported, input is a vector!');
    }

    const size = this.property('size', 0) / 8;
    this.setValue(crypto.randomBytes(size).toString('hex'));
  }

  /**
   * Implements input's validations
   *
   * @param {null|number} at - index used when input has been created as a vector that
   * tells which value should be used
   * @return {Promise<*>} value held by the input based on the current context (at)
   * @protected
   */
  _validation(at){

    // calling super class validations
    return super._validation(at).then((value) => {

      // size checking
      if (this.property('size') !== (value.length / 2) * 8){
        throw new ValidationFail('Invalid hash value size', Hash.errorCodes[0]);
      }

      return value;
    });
  }

  static errorCodes = [
    'c83d98b0-8409-47c3-89c9-f81452c910c0',
  ];
}

// registering the input
Input.registerInput(Hash);

module.exports = Hash;
<commit_msg>removed invalid comment<commit_after>const crypto = require('crypto');
const TypeCheck = require('js-typecheck');
const ValidationFail = require('../../Error/ValidationFail');
const Input = require('../../Input');
const Hex = require('./Hex');

/**
 * Hash input.
 *
 * ```javascript
 * const input = Input.create('myInput: hash', {size: 64});
 * input.setValue('b542f74248124c32');
 * ```
 *
 *
Property Summary

 *
 * Property Name | Description | Defined by Default | Default Value
 * --- | --- | :---: | :---:
 * size | size of the hash in bits. This property needs to be defined at constructor \
 *  time | ::true:: | ::none::
 *
 * All properties including the inherited ones can be listed via
 * {@link registeredPropertyNames}
 */
class Hash extends Hex{

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

    // making sure 'size' property has been defined
    if (TypeCheck.isNone(this.property('size'))){
      throw new Error(`Can't create a hash input (${this.name()}) without the 'size' property!`);
    }

    // locking the 'size' property
    this.lockProperty('size');
  }

  /**
   * Generates a new random hash and assigns it to the value of the input. This method
   * is not supported by vector inputs.
   */
  setRandom(){
    if (this.isVector()){
      throw new Error('Not supported, input is a vector!');
    }

    const size = this.property('size', 0) / 8;
    this.setValue(crypto.randomBytes(size).toString('hex'));
  }

  /**
   * Implements input's validations
   *
   * @param {null|number} at - index used when input has been created as a vector that
   * tells which value should be used
   * @return {Promise<*>} value held by the input based on the current context (at)
   * @protected
   */
  _validation(at){

    // calling super class validations
    return super._validation(at).then((value) => {

      // size checking
      if (this.property('size') !== (value.length / 2) * 8){
        throw new ValidationFail('Invalid hash value size', Hash.errorCodes[0]);
      }

      return value;
    });
  }

  static errorCodes = [
    'c83d98b0-8409-47c3-89c9-f81452c910c0',
  ];
}

// registering the input
Input.registerInput(Hash);

module.exports = Hash;
<|endoftext|>


<commit_before>import React from 'react';
import Relay from 'react-relay';
import { Button, Dialog, DialogTitle, DialogContent, DialogActions, Textfield} from 'react-mdl';
import AddUserMutation from './mutations/AddUserMutation';
import UpdateUserMutation from './mutations/UpdateUserMutation';

export default class UserDialog extends React.Component {

  static propTypes = {
    show: React.PropTypes.bool.isRequired,
    handleState: React.PropTypes.func.isRequired,
    viewer: React.PropTypes.object.isRequired,
    user: React.PropTypes.object
  };

  constructor(props) {
    super(props);

    if(props.user && props.user.id){
      this.state = props.user;
    }else{
      this.state = {id: '', name: '', address: '', email: '', age: ''};
    }

    this.handleOpenDialog = this.handleOpenDialog.bind(this);
    this.handleCloseDialog = this.handleCloseDialog.bind(this);
    this.handleSave = this.handleSave.bind(this);

    this.ocName = this.ocName.bind(this);
    this.ocAddress = this.ocAddress.bind(this);
    this.ocEmail = this.ocEmail.bind(this);
    this.ocAge = this.ocAge.bind(this);
  }

  set(k, v){
    let state = {};
    state[k] = v;
    this.setState(state);
  }

  handleOpenDialog() {
    this.props.handleState(true);
  }

  handleCloseDialog() {
    this.props.handleState(false);
  }

  handleSave(update) {
    let MutationType = update ? UpdateUserMutation : AddUserMutation;
    Relay.Store.commitUpdate(
      new MutationType({
        viewerId: this.props.viewer.id,
        id: this.state.id,
        name: this.state.name,
        address: this.state.address,
        email: this.state.email,
        age: this.state.age
      }), {
        onSuccess: () => {
          this.props.handleState(false);
          console.log(this.state.id);
          if(!this.state.id) this.resetInput();
        },
        onFailure: (t) => {
          console.log(t.getError());
        }
      }
    );
  }

  resetInput(){
    this.set('name', '');
    this.set('address', '');
    this.set('email', '');
    this.set('age', '');
  }

  ocName(e){
    this.set('name', e.target.value);
  }
  ocAddress(e){
    this.set('address', e.target.value);
  }
  ocEmail(e){
    this.set('email', e.target.value);
  }
  ocAge(e){
    this.set('age', e.target.value);
  }

  render() {
    return (
      <Dialog open={this.props.show} style={{width: '400px'}}>
        <DialogTitle>{this.state.id ? 'Edit \'' + this.state.name +'\'' : 'Add new user'}</DialogTitle>
        <DialogContent>

            <Textfield
              floatingLabel
              onChange={this.ocName}
              label="Name"
              ref="name"
              style={{width: '400px'}}
              value={this.state.name}
              required
            />
            <Textfield
              floatingLabel
              onChange={this.ocAddress}
              label="Address"
              style={{width: '400px'}}
              value={this.state.address}
            />
            <Textfield
              floatingLabel
              onChange={this.ocEmail}
              label="Email"
              pattern='\S+@\S+\.\S+'
              error="Not a valid email address"
              style={{width: '400px'}}
              value={this.state.email}
              required
            />
            <Textfield
              floatingLabel
              onChange={this.ocAge}
              label="Age"
              pattern="-?[0-9]*(\.[0-9]+)?"
              error="Age can only be a number!"
              style={{width: '400px'}}
              value={this.state.age}
              required
            />

        </DialogContent>
        <DialogActions>
          <Button type='button' onClick={() => this.handleSave(!!this.state.id)}>
            { this.state.id ? 'Update' : 'Create'}
            </Button>
          <Button type='button' onClick={this.handleCloseDialog}>
            Cancel
          </Button>
        </DialogActions>
      </Dialog>
    );
  }
}
<commit_msg>Added error message on user dialog<commit_after>import React from 'react';
import Relay from 'react-relay';
import { Button, Dialog, DialogTitle, DialogContent, DialogActions, Textfield, Snackbar } from 'react-mdl';
import AddUserMutation from './mutations/AddUserMutation';
import UpdateUserMutation from './mutations/UpdateUserMutation';

export default class UserDialog extends React.Component {

  static propTypes = {
    show: React.PropTypes.bool.isRequired,
    handleState: React.PropTypes.func.isRequired,
    viewer: React.PropTypes.object.isRequired,
    user: React.PropTypes.object
  };

  constructor(props) {
    super(props);

    if(props.user && props.user.id){
      this.state = props.user;
    }else{
      this.state = {id: '', name: '', address: '', email: '', age: ''};
    }

    this.handleOpenDialog = this.handleOpenDialog.bind(this);
    this.handleCloseDialog = this.handleCloseDialog.bind(this);
    this.handleSave = this.handleSave.bind(this);

    this.ocName = this.ocName.bind(this);
    this.ocAddress = this.ocAddress.bind(this);
    this.ocEmail = this.ocEmail.bind(this);
    this.ocAge = this.ocAge.bind(this);

    this.handleShowSnackbar = this.handleShowSnackbar.bind(this);
  }

  set(k, v){
    let state = {};
    state[k] = v;
    this.setState(state);
  }

  handleOpenDialog() {
    this.props.handleState(true);
  }

  handleCloseDialog() {
    this.props.handleState(false);
  }

  handleSave(update) {
    let MutationType = update ? UpdateUserMutation : AddUserMutation;
    Relay.Store.commitUpdate(
      new MutationType({
        viewerId: this.props.viewer.id,
        id: this.state.id,
        name: this.state.name,
        address: this.state.address,
        email: this.state.email,
        age: this.state.age
      }), {
        onSuccess: () => {
          this.props.handleState(false);
          console.log(this.state.id);
          if(!this.state.id) this.resetInput();
        },
        onFailure: (t) => {
          this.handleShowSnackbar();
          this.setState({message: t.getError().source.errors[0].message});
        }
      }
    );
  }

  resetInput(){
    this.set('name', '');
    this.set('address', '');
    this.set('email', '');
    this.set('age', '');
  }

  ocName(e){
    this.set('name', e.target.value);
  }
  ocAddress(e){
    this.set('address', e.target.value);
  }
  ocEmail(e){
    this.set('email', e.target.value);
  }
  ocAge(e){
    this.set('age', e.target.value);
  }

  handleShowSnackbar() {
    this.setState({ isSnackbarActive: true });
  }

  render() {
    return (
      <Dialog open={this.props.show} style={{width: '400px'}}>
        <DialogTitle>{this.state.id ? 'Edit \'' + this.state.name +'\'' : 'Add new user'}</DialogTitle>
        <DialogContent>

            <Snackbar
              active={!!this.state.isSnackbarActive}
              onTimeout={() => this.setState({ isSnackbarActive: false })}>
                {this.state.message}
            </Snackbar>
            <Textfield
              floatingLabel
              onChange={this.ocName}
              label="Name"
              ref="name"
              style={{width: '400px'}}
              value={this.state.name}
              required
            />
            <Textfield
              floatingLabel
              onChange={this.ocAddress}
              label="Address"
              style={{width: '400px'}}
              value={this.state.address}
            />
            <Textfield
              floatingLabel
              onChange={this.ocEmail}
              label="Email"
              pattern='\S+@\S+\.\S+'
              error="Not a valid email address"
              style={{width: '400px'}}
              value={this.state.email}
              required
            />
            <Textfield
              floatingLabel
              onChange={this.ocAge}
              label="Age"
              pattern="-?[0-9]*(\.[0-9]+)?"
              error="Age can only be a number!"
              style={{width: '400px'}}
              value={this.state.age}
              required
            />

        </DialogContent>
        <DialogActions>
          <Button type='button' onClick={() => this.handleSave(!!this.state.id)}>
            { this.state.id ? 'Update' : 'Create'}
            </Button>
          <Button type='button' onClick={this.handleCloseDialog}>
            Cancel
          </Button>
        </DialogActions>
      </Dialog>
    );
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-dom/test-utils';

import Affix from '../src/Affix';

import { render } from './helpers';

describe('<Affix>', () => {
  let mountPoint;
  let handlers;

  // This makes the window very tall; hopefully enough to exhibit the affix
  // behavior. If this is insufficient, we should modify the Karma config to
  // fix the browser window size.
  class Container extends React.Component {
    render() {
      return (

          Placeholder

          {this.props.children}

      );
    }
  }

  class Content extends React.Component {
    render() {
      ++Content.renderCount;
      return
Content
;
    }
  }

  beforeEach(() => {
    Content.renderCount = 0;
    mountPoint = document.createElement('div');
    document.body.appendChild(mountPoint);
    handlers = {
      onAffix: sinon.spy(),
      onAffixed: sinon.spy(),
      onAffixTop: sinon.spy(),
      onAffixedTop: sinon.spy(),
      onAffixBottom: sinon.spy(),
      onAffixedBottom: sinon.spy()
    };
  });

  afterEach(() => {
    Object.keys(handlers).forEach(key => handlers[key].reset());
    ReactDOM.unmountComponentAtNode(mountPoint);
    document.body.removeChild(mountPoint);
    window.scrollTo(0, 0);
  });

  it('should render the affix content', () => {
    let instance = render((
      <Affix>
        <Content />
      </Affix>
    ), mountPoint);

    const content =
      ReactTestUtils.findRenderedComponentWithType(instance, Content);

    expect(content).to.exist;
  });

  describe('no viewportOffsetTop', () => {
    let node;

    beforeEach(() => {
      const container = render((
        <Container>
          <Affix
            offsetTop={100}
            offsetBottom={10000}
            topClassName="affix-top"
            topStyle={{color: 'red'}}
            affixClassName="affix"
            affixStyle={{color: 'white'}}
            bottomClassName="affix-bottom"
            bottomStyle={{color: 'blue'}}
            {...handlers}
          >
            <Content style={{height: 100}} />
          </Affix>
        </Container>
      ), mountPoint);

      node = ReactDOM.findDOMNode(ReactTestUtils.findRenderedComponentWithType(
        container, Content
      ));
    });

    it('should render correctly at top', (done) => {
      window.scrollTo(0, 101);

      requestAnimationFrame(() => {
        window.scrollTo(0, 99);
        requestAnimationFrame(() => {
          expect(node.className).to.equal('affix-top');
          expect(node.style.position).to.not.be.ok;
          expect(node.style.top).to.not.be.ok;
          expect(node.style.color).to.equal('red');
          expect(handlers.onAffixTop).to.been.calledOnce;
          expect(handlers.onAffixedTop).to.been.calledOnce;
          done();
        });
      });
    });

    it('should affix correctly', (done) => {
      window.scrollTo(0, 101);
      requestAnimationFrame(() => {
        expect(node.className).to.equal('affix');
        expect(node.style.position).to.equal('fixed');
        expect(node.style.top).to.not.be.ok;
        expect(node.style.color).to.equal('white');

        expect(handlers.onAffix).to.been.calledOnce;
        expect(handlers.onAffixed).to.been.calledOnce;
        done();
      });
    });

    it('should render correctly at bottom', (done) => {
      window.scrollTo(0, 20000);
      requestAnimationFrame(() => {
        expect(node.className).to.equal('affix-bottom');
        expect(node.style.position).to.equal('absolute');
        expect(node.style.top).to.equal('9900px');
        expect(node.style.color).to.equal('blue');

        expect(handlers.onAffixBottom).to.been.calledOnce;
        expect(handlers.onAffixedBottom).to.been.calledOnce;
        done();
      });
    });
  });

  describe('with viewportOffsetTop', () => {
    let node;

    beforeEach(() => {
      const container = render((
        <Container>
          <Affix
            offsetTop={100}
            viewportOffsetTop={50}
            {...handlers}
          >
            <Content />
          </Affix>
        </Container>
      ), mountPoint);

      node = ReactDOM.findDOMNode(ReactTestUtils.findRenderedComponentWithType(
        container, Content
      ));
    });

    it('should render correctly at top', (done) => {
      window.scrollTo(0, 49);

      requestAnimationFrame(() => {
        expect(node.style.position).to.not.be.ok;
        expect(node.style.top).to.not.be.ok;
        done();
      });
    });

    it('should affix correctly', (done) => {
      window.scrollTo(0, 51);
      requestAnimationFrame(() => {
        expect(node.style.position).to.equal('fixed');
        expect(node.style.top).to.equal('50px');
        done();
      });
    });
  });

  describe('re-rendering optimizations', () => {
    beforeEach(() => {
      render((
        <Container>
          <Affix
            offsetTop={100}
            offsetBottom={10000}
          >
            <Content />
          </Affix>
        </Container>
      ), mountPoint);
    });

    it('should avoid re-rendering at top', (done) => {
      expect(Content.renderCount).to.equal(1);

      window.scrollTo(0, 50);
      requestAnimationFrame(() => {
        expect(Content.renderCount).to.equal(1);
        done();
      });
    });

    it('should avoid re-rendering when affixed', (done) => {
      expect(Content.renderCount).to.equal(1);

      window.scrollTo(0, 1000);
      requestAnimationFrame(() => {
        expect(Content.renderCount).to.equal(2);

        window.scrollTo(0, 2000);
        requestAnimationFrame(() => {
          expect(Content.renderCount).to.equal(2);
          done();
        });
      });
    });

    it('should avoid re-rendering at bottom', (done) => {
      expect(Content.renderCount).to.equal(1);

      window.scrollTo(0, 15000);
      requestAnimationFrame(() => {
        expect(Content.renderCount).to.equal(3);

        window.scrollTo(0, 16000);
        requestAnimationFrame(() => {
          expect(Content.renderCount).to.equal(3);
          done();
        });
      });
    });
  });
});
<commit_msg>fix tests<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-dom/test-utils';

import Affix from '../src/Affix';

import { render, injectCss } from './helpers';

describe('<Affix>', () => {
  let mountPoint;
  let handlers;

  before(() => {
    injectCss(`
      html, body { margin: 0; padding: 0; }
    `)
  })

  after(() => {
    injectCss.reset()
  })
  // This makes the window very tall; hopefully enough to exhibit the affix
  // behavior. If this is insufficient, we should modify the Karma config to
  // fix the browser window size.
  class Container extends React.Component {
    render() {
      return (

          Placeholder

          {this.props.children}

      );
    }
  }

  class Content extends React.Component {
    render() {
      ++Content.renderCount;
      return
Content
;
    }
  }

  beforeEach(() => {
    Content.renderCount = 0;

    mountPoint = document.createElement('div');
    document.body.appendChild(mountPoint);
    handlers = {
      onAffix: sinon.spy(),
      onAffixed: sinon.spy(),
      onAffixTop: sinon.spy(),
      onAffixedTop: sinon.spy(),
      onAffixBottom: sinon.spy(),
      onAffixedBottom: sinon.spy()
    };
  });

  afterEach(() => {
    Object.keys(handlers).forEach(key => handlers[key].reset());
    ReactDOM.unmountComponentAtNode(mountPoint);
    document.body.removeChild(mountPoint);
    window.scrollTo(0, 0);
  });

  it('should render the affix content', () => {
    let instance = render((
      <Affix>
        <Content />
      </Affix>
    ), mountPoint);

    const content =
      ReactTestUtils.findRenderedComponentWithType(instance, Content);

    expect(content).to.exist;
  });

  describe('no viewportOffsetTop', () => {
    let node;

    beforeEach(() => {
      const container = render((
        <Container>
          <Affix
            offsetTop={100}
            offsetBottom={10000}
            topClassName="affix-top"
            topStyle={{color: 'red'}}
            affixClassName="affix"
            affixStyle={{color: 'white'}}
            bottomClassName="affix-bottom"
            bottomStyle={{color: 'blue'}}
            {...handlers}
          >
            <Content style={{height: 100}} />
          </Affix>
        </Container>
      ), mountPoint);

      node = ReactDOM.findDOMNode(ReactTestUtils.findRenderedComponentWithType(
        container, Content
      ));
    });

    it('should render correctly at top', (done) => {
      window.scrollTo(0, 101);

      requestAnimationFrame(() => {
        window.scrollTo(0, 99);
        requestAnimationFrame(() => {
          expect(node.className).to.equal('affix-top');
          expect(node.style.position).to.not.be.ok;
          expect(node.style.top).to.not.be.ok;
          expect(node.style.color).to.equal('red');
          expect(handlers.onAffixTop).to.been.calledOnce;
          expect(handlers.onAffixedTop).to.been.calledOnce;
          done();
        });
      });
    });

    it('should affix correctly', (done) => {
      window.scrollTo(0, 101);
      requestAnimationFrame(() => {
        expect(node.className).to.equal('affix');
        expect(node.style.position).to.equal('fixed');
        expect(node.style.top).to.not.be.ok;
        expect(node.style.color).to.equal('white');

        expect(handlers.onAffix).to.been.calledOnce;
        expect(handlers.onAffixed).to.been.calledOnce;
        done();
      });
    });

    it('should render correctly at bottom', (done) => {
      window.scrollTo(0, 20000);
      requestAnimationFrame(() => {
        expect(node.className).to.equal('affix-bottom');
        expect(node.style.position).to.equal('absolute');
        expect(node.style.top).to.equal('9900px');
        expect(node.style.color).to.equal('blue');

        expect(handlers.onAffixBottom).to.been.calledOnce;
        expect(handlers.onAffixedBottom).to.been.calledOnce;
        done();
      });
    });
  });

  describe('with viewportOffsetTop', () => {
    let node;

    beforeEach(() => {
      const container = render((
        <Container>
          <Affix
            offsetTop={100}
            viewportOffsetTop={50}
            {...handlers}
          >
            <Content />
          </Affix>
        </Container>
      ), mountPoint);

      node = ReactDOM.findDOMNode(ReactTestUtils.findRenderedComponentWithType(
        container, Content
      ));
    });

    it('should render correctly at top', (done) => {
      window.scrollTo(0, 49);

      requestAnimationFrame(() => {
        expect(node.style.position).to.not.be.ok;
        expect(node.style.top).to.not.be.ok;
        done();
      });
    });

    it('should affix correctly', (done) => {
      window.scrollTo(0, 51);
      requestAnimationFrame(() => {
        expect(node.style.position).to.equal('fixed');
        expect(node.style.top).to.equal('50px');
        done();
      });
    });
  });

  describe('re-rendering optimizations', () => {
    beforeEach(() => {
      render((
        <Container>
          <Affix
            offsetTop={100}
            offsetBottom={10000}
          >
            <Content />
          </Affix>
        </Container>
      ), mountPoint);
    });

    it('should avoid re-rendering at top', (done) => {
      expect(Content.renderCount).to.equal(1);

      window.scrollTo(0, 50);
      requestAnimationFrame(() => {
        expect(Content.renderCount).to.equal(1);
        done();
      });
    });

    it('should avoid re-rendering when affixed', (done) => {
      expect(Content.renderCount).to.equal(1);

      window.scrollTo(0, 1000);
      requestAnimationFrame(() => {
        expect(Content.renderCount).to.equal(2);

        window.scrollTo(0, 2000);
        requestAnimationFrame(() => {
          expect(Content.renderCount).to.equal(2);
          done();
        });
      });
    });

    it('should avoid re-rendering at bottom', (done) => {
      expect(Content.renderCount).to.equal(1);

      window.scrollTo(0, 15000);
      requestAnimationFrame(() => {
        expect(Content.renderCount).to.equal(3);

        window.scrollTo(0, 16000);
        requestAnimationFrame(() => {
          expect(Content.renderCount).to.equal(3);
          done();
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>export { default } from '../../../../../../../../lib/mass-tools/0.61.2/mass-tools.min';
// } from 'http://localhost:9898/cheminfo-js/molecular-formula/dist/molecular-formula.js';
<commit_msg>feat: use mass-tools 0.63.0<commit_after>export { default } from '../../../../../../../../lib/mass-tools/0.63.0/mass-tools.min';
// } from 'http://localhost:9898/cheminfo-js/molecular-formula/dist/molecular-formula.js';
<|endoftext|>


<commit_before>
define([
    'src/util/api',
    './PrinterInstance',
    './printProcessors',
    './PrintServer',
    '../../../rest-on-couch/Roc'
]
    , function (API, Printer, processors, PrintServer, Roc) {
    const SECOND = 1000;
    const MINUTE = 60 * SECOND;
    const LIMIT = 30 * MINUTE;
    return async function (opts) {
        var printerRoc, formatsRoc, printServerRoc, printers, printFormats, printServers, allIds;
        var onlineServers, onlinePrinters;

        const exports = {
            getDBPrinters() {
                return printers;
            },

            async refresh() {
                allIds = new Set();
                printerRoc = new Roc(opts);
                formatsRoc = new Roc(opts);
                printServerRoc = new Roc(opts);
                printers = await printerRoc.view('entryByKind', {
                    key: 'printer',
                    varName: 'labelPrinters',
                    sort: (a, b) => b.$modificationDate - a.$modificationDate
                });
                printFormats = await formatsRoc.view('entryByKind', {
                    key: 'printFormat',
                    varName: 'labelPrintFormats',
                    sort: (a, b) => b.$modificationDate - a.$modificationDate
                });

                printServers = await printServerRoc.view('printServerByMacAddress', {
                    varName: 'printServers',
                    sort: (a, b) => b.$modificationDate - a.$modificationDate
                });
                onlineServers = printServers.filter(ps => Date.now() - ps.$modificationDate < LIMIT);
                onlinePrinters = printers.filter(p => onlineServers.find(ps => ps.$content.macAddress === p.$content.macAddress));

                await Promise.all(onlineServers.map(ps => {
                    return exports.getConnectedPrinters(ps.$content).then(ids => {
                        ps.ids = ids;
                        ids.forEach(id => allIds.add(id));
                        ps.responds = true;
                        ps.color = 'lightgreen';
                    }).catch(() => {
                        ps.ids = [];
                        ps.responds = false;
                        ps.color = 'pink';
                    }).then(() => {
                        ps.triggerChange();
                    });
                }));

                API.createData('allIds', Array.from(allIds));
            },

            async getConnectedPrinters(s) {
                const server = new PrintServer(s, opts);
                return server.getDeviceIds();
            },

            async print(printer, printFormat, data) {
                printer = await printerRoc.get(printer);
                printFormat = await formatsRoc.get(printFormat);
                const printServer = printServers.find(ps => String(ps.$content.macAddress) === String(printer.$content.macAddress));
                const p = new Printer(printer.$content, printServer.$content, opts);
                await p.print(printFormat.$content, data);
            },

            async createPrinter(printer) {
                printer.$kind = 'printer';
                await printerRoc.create(printer);
            },

            async createFormat(format) {
                format.$kind = 'printFormat';
                await formatsRoc.create(format);
            },

            async updateFormat(format) {
                await formatsRoc.update(format);
            },

            async updatePrinter(printer) {
                await printerRoc.update(printer);
            },

            async deletePrinter(printer) {
                await printerRoc.delete(printer);
            },

            async deleteFormat(format) {
                await formatsRoc.delete(format);
            },

                // get online printers that can print a given format
            async getPrinters(format) {
                if (!format) return onlinePrinters;
                format = await formatsRoc.get(format);
                const onlineMacAdresses = onlinePrinters
                        .map(ps => ps.$content.macAddress);
                return printers
                        .filter(p => onlineMacAdresses.includes(p.$content.macAddress))
                        .filter(p => {
                            return format.$content.models.filter(m => String(m.name) === String(p.$content.model)).length > 0;
                        });
            },

            async getFormats(printer, type) {
                if (!printer) {
                    var formats = printFormats.filter(f => {
                        return onlinePrinters.some(printer => f.$content.models.some(m => String(m.name) === String(printer.$content.model)));
                    });
                } else {
                    printer = await printerRoc.get(printer);
                    formats = printFormats.filter(f => f.$content.models.some(m => String(m.name) === String(printer.$content.model)));
                }
                if (type) {
                    formats = formats.filter(f => String(f.$content.type) === type);
                }
                return formats;
            },

            getProcessors() {
                return Object.keys(processors);
            },

            async getTypes() {
                var formats = await exports.getFormats.apply(null, arguments);
                var s = new Set();
                for (var format of formats) {
                    s.add(String(format.$content.type));
                }
                return Array.from(s);
            }
        };

        await exports.refresh();
        return exports;
    };

});
<commit_msg>shorter limit for printer to be considered offline<commit_after>
define([
    'src/util/api',
    './PrinterInstance',
    './printProcessors',
    './PrintServer',
    '../../../rest-on-couch/Roc'
]
    , function (API, Printer, processors, PrintServer, Roc) {
    const SECOND = 1000;
    const MINUTE = 60 * SECOND;
    const LIMIT = 11 * MINUTE;
    return async function (opts) {
        var printerRoc, formatsRoc, printServerRoc, printers, printFormats, printServers, allIds;
        var onlineServers, onlinePrinters;

        const exports = {
            getDBPrinters() {
                return printers;
            },

            async refresh() {
                allIds = new Set();
                printerRoc = new Roc(opts);
                formatsRoc = new Roc(opts);
                printServerRoc = new Roc(opts);
                printers = await printerRoc.view('entryByKind', {
                    key: 'printer',
                    varName: 'labelPrinters',
                    sort: (a, b) => b.$modificationDate - a.$modificationDate
                });
                printFormats = await formatsRoc.view('entryByKind', {
                    key: 'printFormat',
                    varName: 'labelPrintFormats',
                    sort: (a, b) => b.$modificationDate - a.$modificationDate
                });

                printServers = await printServerRoc.view('printServerByMacAddress', {
                    varName: 'printServers',
                    sort: (a, b) => b.$modificationDate - a.$modificationDate
                });
                onlineServers = printServers.filter(ps => Date.now() - ps.$modificationDate < LIMIT);
                onlinePrinters = printers.filter(p => onlineServers.find(ps => ps.$content.macAddress === p.$content.macAddress));

                await Promise.all(onlineServers.map(ps => {
                    return exports.getConnectedPrinters(ps.$content).then(ids => {
                        ps.ids = ids;
                        ids.forEach(id => allIds.add(id));
                        ps.responds = true;
                        ps.color = 'lightgreen';
                    }).catch(() => {
                        ps.ids = [];
                        ps.responds = false;
                        ps.color = 'pink';
                    }).then(() => {
                        ps.triggerChange();
                    });
                }));

                API.createData('allIds', Array.from(allIds));
            },

            async getConnectedPrinters(s) {
                const server = new PrintServer(s, opts);
                return server.getDeviceIds();
            },

            async print(printer, printFormat, data) {
                printer = await printerRoc.get(printer);
                printFormat = await formatsRoc.get(printFormat);
                const printServer = printServers.find(ps => String(ps.$content.macAddress) === String(printer.$content.macAddress));
                const p = new Printer(printer.$content, printServer.$content, opts);
                await p.print(printFormat.$content, data);
            },

            async createPrinter(printer) {
                printer.$kind = 'printer';
                await printerRoc.create(printer);
            },

            async createFormat(format) {
                format.$kind = 'printFormat';
                await formatsRoc.create(format);
            },

            async updateFormat(format) {
                await formatsRoc.update(format);
            },

            async updatePrinter(printer) {
                await printerRoc.update(printer);
            },

            async deletePrinter(printer) {
                await printerRoc.delete(printer);
            },

            async deleteFormat(format) {
                await formatsRoc.delete(format);
            },

                // get online printers that can print a given format
            async getPrinters(format) {
                if (!format) return onlinePrinters;
                format = await formatsRoc.get(format);
                const onlineMacAdresses = onlinePrinters
                        .map(ps => ps.$content.macAddress);
                return printers
                        .filter(p => onlineMacAdresses.includes(p.$content.macAddress))
                        .filter(p => {
                            return format.$content.models.filter(m => String(m.name) === String(p.$content.model)).length > 0;
                        });
            },

            async getFormats(printer, type) {
                if (!printer) {
                    var formats = printFormats.filter(f => {
                        return onlinePrinters.some(printer => f.$content.models.some(m => String(m.name) === String(printer.$content.model)));
                    });
                } else {
                    printer = await printerRoc.get(printer);
                    formats = printFormats.filter(f => f.$content.models.some(m => String(m.name) === String(printer.$content.model)));
                }
                if (type) {
                    formats = formats.filter(f => String(f.$content.type) === type);
                }
                return formats;
            },

            getProcessors() {
                return Object.keys(processors);
            },

            async getTypes() {
                var formats = await exports.getFormats.apply(null, arguments);
                var s = new Set();
                for (var format of formats) {
                    s.add(String(format.$content.type));
                }
                return Array.from(s);
            }
        };

        await exports.refresh();
        return exports;
    };

});
<|endoftext|>


<commit_before>// - load cookies on request start
// - add helper to store cookies
// - autosave cookies on request end
//

'use strict';

var _      = require('lodash');
var cookie = require('cookie');

module.exports = function (N) {

  // Parse cookies
  //
  N.wire.before('server_chain:*', { priority: -85 }, function cookies_start(env, callback) {

    // Helper to read incoming cookies from client.
    function getCookie(name) {
      return getCookie.storage[name];
    }
    getCookie.storage = {};

    // Helper to write outgoing cookies from server.
    function setCookie(name, value, options) {
      setCookie.storage[name] = { value: value, options: options || {} };
    }
    setCookie.storage = {};

    // Helper to change value or options of already created outgoing cookie.
    function updateCookie(name, options) {
      options = options || {};

      var cookie = setCookie.storage[name];

      if (cookie) {
        cookie.value = options.value || cookie.value;
        _.extend(cookie.options, _.omit(options, 'value'));
      } else {
        N.logger.error('Unable to update non-existent outgoing cookie %j', name);
      }
    }

    // Parse incoming request cookies.
    if (env.origin.req.headers.cookie) {
      try {
        getCookie.storage = cookie.parse(env.origin.req.headers.cookie);
      } catch (err) {
        callback(err);
        return;
      }
    }

    // Expose.
    env.extras.getCookie    = getCookie;
    env.extras.setCookie    = setCookie;
    env.extras.updateCookie = updateCookie;
    callback();
  });

  // Send cookies that must be set on the client
  //
  N.wire.after('server_chain:*', { priority: 90, ensure: true }, function cookies_end(env, callback) {
    var cookies = [];

    // prepare list of cookies to be sent
    _.each(env.extras.setCookie.storage, function (data, name) {
      var options = _.extend({ httpOnly: true }, data.options);
      cookies.push(cookie.serialize(name, data.value, options));
    });

    env.headers['Set-Cookie'] = cookies;
    callback();
  });
};
<commit_msg>Decrease priority of `cookies_start` hook.<commit_after>// - load cookies on request start
// - add helper to store cookies
// - autosave cookies on request end
//

'use strict';

var _      = require('lodash');
var cookie = require('cookie');

module.exports = function (N) {

  // Parse cookies
  //
  N.wire.before('server_chain:*', { priority: -90 }, function cookies_start(env, callback) {

    // Helper to read incoming cookies from client.
    function getCookie(name) {
      return getCookie.storage[name];
    }
    getCookie.storage = {};

    // Helper to write outgoing cookies from server.
    function setCookie(name, value, options) {
      setCookie.storage[name] = { value: value, options: options || {} };
    }
    setCookie.storage = {};

    // Helper to change value or options of already created outgoing cookie.
    function updateCookie(name, options) {
      options = options || {};

      var cookie = setCookie.storage[name];

      if (cookie) {
        cookie.value = options.value || cookie.value;
        _.extend(cookie.options, _.omit(options, 'value'));
      } else {
        N.logger.error('Unable to update non-existent outgoing cookie %j', name);
      }
    }

    // Parse incoming request cookies.
    if (env.origin.req.headers.cookie) {
      try {
        getCookie.storage = cookie.parse(env.origin.req.headers.cookie);
      } catch (err) {
        callback(err);
        return;
      }
    }

    // Expose.
    env.extras.getCookie    = getCookie;
    env.extras.setCookie    = setCookie;
    env.extras.updateCookie = updateCookie;
    callback();
  });

  // Send cookies that must be set on the client
  //
  N.wire.after('server_chain:*', { priority: 90, ensure: true }, function cookies_end(env, callback) {
    var cookies = [];

    // prepare list of cookies to be sent
    _.each(env.extras.setCookie.storage, function (data, name) {
      var options = _.extend({ httpOnly: true }, data.options);
      cookies.push(cookie.serialize(name, data.value, options));
    });

    env.headers['Set-Cookie'] = cookies;
    callback();
  });
};
<|endoftext|>


<commit_before>"use strict";

var dummy
  , hotplate =  require('hotplate')
  , path = require('path')
;

// Some defaults to get things going
//  * needs to be asyncronous, AND
//  * needs to have the package "hotplate" set
//  * needs to have a "local" blank.html file configured via dojoBlankHtmlUrl
hotplate.config.set( 'hotDojoAdd.dojoConfig', {
  packages: [
    {
      name: 'hotplate',
      location: hotplate.config.get( 'hotplate.moduleFilesPrefix' ),
    },
  ],
  dojoBlankHtmlUrl: path.join( hotplate.config.get( 'hotplate.moduleFilesPrefix' ), 'hotDojo', 'blank.html' ),
  async: 1,
});

// By default, take Dojo from the CDN. It will always work. Developers will set
// a direct path for Dojo in their local machine so that debugging is easier
hotplate.config.set( 'hotDojoAdd.dojoUrl', "//ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js.uncompressed.js" );
hotplate.config.set( 'hotDojoAdd.cssUrl',  "//ajax.googleapis.com/ajax/libs/dojo/1.8.3/dijit/themes/claro/claro.css" );
hotplate.config.set( 'hotDojoAdd.bodyClass',  "claro" );

hotplate.hotEvents.on( 'pageElements', 'hotDojoAdd', hotplate.cachable( function( done ){

  var headLines, dojoConfigAssign;

  hotplate.hotEvents.emit( 'enrichDojoConf', hotplate.config.get( 'hotDojoAdd.dojoConfig'), function( err, results ){

    if(err){
      done( err );
    } else {

      // Make up the string to add to the page
      dojoConfigAssign = "<script>dojoConfig = " + JSON.stringify( hotplate.config.get( 'hotDojoAdd.dojoConfig') ) + "</script>"; // MAYBE ADD replace(/\//g,'\\/').

      headLines = [
        dojoConfigAssign,
        '<link type="text/css" rel="stylesheet" href="' + hotplate.config.get( 'hotDojoAdd.cssUrl' ) + '" media="screen" />',
        '<script src="' + hotplate.config.get( 'hotDojoAdd.dojoUrl' ) + '"></script>',
      ];

      done( null, { headLines: headLines } );
    }
  });

}));

hotplate.hotEvents.on( 'pageElementsPerPage', 'hotDojoAdd', function( req, pageName, done ){

  hotplate.hotEvents.emit( 'dojoModulesPerPage', req, pageName, function( err, dojoModules ){
    if( err ){
      done( err );
    } else {

      var result = '';

      result += '<script>\n';
      result += '  // Modules marked as to be loaded by hotplate\n';
      result += '  require(["dojo/topic", "dojo/dom-class", "dojo/_base/window", ';

      // Make up the list of modules as a comma separated string, depending on what returned
      var listAsArray = [];
      dojoModules.forEach( function( moduleInfo ){
        moduleInfo.result.forEach( function( moduleResult ){
          listAsArray.push( '"' + path.join( 'hotplate', moduleInfo.module, moduleResult) + '"');
        });
      });
      result += listAsArray.join() + ', "dojo/domReady!"], function(topic, domClass, win ){\n';

      result += '\n';
      result += '    // This topic will tell hotplate modules that everything has been loaded up\n';

      result += '    domClass.add( win.body(), "claro" );\n';
      result += '    topic.publish( "hotplateModulesLoaded" );\n';
      result += '  })\n';
      result += '</script>\n';

      done( null, { headLines: [ result ] } );
    }
  });
});

// Simply activate path to client files
hotplate.hotEvents.on( 'clientPath', 'hotDojo', function( done ){
  done( null, path.join(__dirname, '../client') );
})

<commit_msg>Newest Dojo in CDN<commit_after>"use strict";

var dummy
  , hotplate =  require('hotplate')
  , path = require('path')
;

// Some defaults to get things going
//  * needs to be asyncronous, AND
//  * needs to have the package "hotplate" set
//  * needs to have a "local" blank.html file configured via dojoBlankHtmlUrl
hotplate.config.set( 'hotDojoAdd.dojoConfig', {
  packages: [
    {
      name: 'hotplate',
      location: hotplate.config.get( 'hotplate.moduleFilesPrefix' ),
    },
  ],
  dojoBlankHtmlUrl: path.join( hotplate.config.get( 'hotplate.moduleFilesPrefix' ), 'hotDojo', 'blank.html' ),
  async: 1,
});

// By default, take Dojo from the CDN. It will always work. Developers will set
// a direct path for Dojo in their local machine so that debugging is easier
hotplate.config.set( 'hotDojoAdd.dojoUrl', "//ajax.googleapis.com/ajax/libs/dojo/1.8.5/dojo/dojo.js.uncompressed.js" );
hotplate.config.set( 'hotDojoAdd.cssUrl',  "//ajax.googleapis.com/ajax/libs/dojo/1.8.5/dijit/themes/claro/claro.css" );
hotplate.config.set( 'hotDojoAdd.bodyClass',  "claro" );

hotplate.hotEvents.on( 'pageElements', 'hotDojoAdd', hotplate.cachable( function( done ){

  var headLines, dojoConfigAssign;

  hotplate.hotEvents.emit( 'enrichDojoConf', hotplate.config.get( 'hotDojoAdd.dojoConfig'), function( err, results ){

    if(err){
      done( err );
    } else {

      // Make up the string to add to the page
      dojoConfigAssign = "<script>dojoConfig = " + JSON.stringify( hotplate.config.get( 'hotDojoAdd.dojoConfig') ) + "</script>"; // MAYBE ADD replace(/\//g,'\\/').

      headLines = [
        dojoConfigAssign,
        '<link type="text/css" rel="stylesheet" href="' + hotplate.config.get( 'hotDojoAdd.cssUrl' ) + '" media="screen" />',
        '<script src="' + hotplate.config.get( 'hotDojoAdd.dojoUrl' ) + '"></script>',
      ];

      done( null, { headLines: headLines } );
    }
  });

}));

hotplate.hotEvents.on( 'pageElementsPerPage', 'hotDojoAdd', function( req, pageName, done ){

  hotplate.hotEvents.emit( 'dojoModulesPerPage', req, pageName, function( err, dojoModules ){
    if( err ){
      done( err );
    } else {

      var result = '';

      result += '<script>\n';
      result += '  // Modules marked as to be loaded by hotplate\n';
      result += '  require(["dojo/topic", "dojo/dom-class", "dojo/_base/window", ';

      // Make up the list of modules as a comma separated string, depending on what returned
      var listAsArray = [];
      dojoModules.forEach( function( moduleInfo ){
        moduleInfo.result.forEach( function( moduleResult ){
          listAsArray.push( '"' + path.join( 'hotplate', moduleInfo.module, moduleResult) + '"');
        });
      });
      result += listAsArray.join() + ', "dojo/domReady!"], function(topic, domClass, win ){\n';

      result += '\n';
      result += '    // This topic will tell hotplate modules that everything has been loaded up\n';

      result += '    domClass.add( win.body(), "claro" );\n';
      result += '    topic.publish( "hotplateModulesLoaded" );\n';
      result += '  })\n';
      result += '</script>\n';

      done( null, { headLines: [ result ] } );
    }
  });
});

// Simply activate path to client files
hotplate.hotEvents.on( 'clientPath', 'hotDojo', function( done ){
  done( null, path.join(__dirname, '../client') );
})

<|endoftext|>


<commit_before>/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community 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://opensource.org/licenses/ECL-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.
 */

/**
 * @RESTModel BasicContent
 *
 * @Required    [created,createdBy,displayName,id,lastModified,latestRevisionId,previews,resourceSubType,tenantAlias,visibility]
 * @Property    {number}    created             Timestamp at which this content item was created in milliseconds since epoch
 * @Property    {string}    createdBy           Unique identifier of the user who created this content item
 * @Property    {string}    description         Description of the content item
 * @Property    {string}    displayName         Title of the content item
 * @Property    {string}    id                  Globally unique content ID for this content item
 * @Property    {number}    lastModified        Timestamp at which this content item was modified for the last time in milliseconds since epoch
 * @Property    {string}    latestRevisionId    Unique identifier of the latest revision associated with this piece of content
 * @Property    {Previews}  previews            Preview data of the latest revision of the content item
 * @Property    {string}    resourceSubType     Type of content item                   [collabdoc,file,link]
 * @Property    {string}    tenantAlias         Alias of the tenant this content item is being created in
 * @Property    {string}    visibility          Visibility of the content item         [loggedin,private,public]
 */

/**
 * @RESTModel CollabDocInfo
 *
 * @Required    [url]
 * @Property    {string}    url                 Internal URL to access collaborative document
 */

/**
 * @RESTModel Content
 *
 * @Required    [canShare,created,createdBy,description,displayName,id,isManager,lastModified,latestRevisionId,previews,profilePath,resourceSubType,resourceType,signature,tenant,visibility]
 * @Property    {boolean}       canShare           Can current user share the content
 * @Property    {number}        created            Timestamp at which this content item was created in milliseconds since epoch
 * @Property    {BasicUser}     createdBy          User who created this content item
 * @Property    {string}        description        Description of the content item
 * @Property    {string}        displayName        Title of the content item
 * @Property    {string}        downloadPath       Relative URL to download content source
 * @Property    {string}        etherpadGroupId    Collaborative document group
 * @Property    {string}        etherpadPadId      Collaborative document id
 * @Property    {string}        filename           Name of content file
 * @Property    {string}        id                 Globally unique content ID for this content item
 * @Property    {boolean}       isManager          Is current user a manager of the content
 * @Property    {number}        lastModified       Timestamp at which this content item was modified for the last time in milliseconds since epoch
 * @Property    {Revision}      latestRevision     Information about latest revision of content item
 * @Property    {String}        latestRevisionId   Unique identifier of the latest revision associated with this piece of content
 * @Property    {string}        mime               MIME type for content (e.g. "text/plain")
 * @Property    {Previews}      previews           Preview data of the latest revision of the content item
 * @Property    {string}        profilePath        Relative URL for content's main page
 * @Property    {string}        resourceSubType    Type of content item                   [collabdoc,file,link]
 * @Property    {string}        resourceType       Type of resource                       [content]
 * @Property    {Signature}     signature          Credentials to access content
 * @Property    {number}        size               Size of content file in bytes
 * @Property    {BasicTenant}   tenant             Information about the tenant this content item is being created in
 * @Property    {string}        visibility         Visibility of the content item         [loggedin,private,public]
 */

/**
 * @RESTModel ContentList
 *
 * @Required    [nextToken,results]
 * @Property    {Content[]}     results                 Array of content items associated with object
 * @Property    {string}        nextToken               Marker to allow continuation of search (empty if search has been exhausted)
 */

/**
 * @RESTModel Permissions
 *
 * @Required    []
 * @Property    {string}        {userId}                Permission for specified user, may be repeated for multiple users [false,manager,viewer]
 */

/**
 * @RESTModel PreviewFile
 *
 * @Required    [filename,size,uri]
 * @Property    {string}            filename            Name of preview file
 * @Property    {string}            size                Size of preview file        [large,medium,small,thumbnail,wide]
 * @Property    {string}            uri                 URI of preview file
 */

/**
 * @RESTModel PreviewInfo
 *
 * @Required    [contentMetadata,links,previewMetadata,sizes]
 * @Property    {UpdatedContent}     contentMetadata    Updated information about content
 * @Property    {PreviewLink}        links              External link to use as preview image
 * @Property    {PreviewMetadata}    previewMetadata    Updated information about preview
 * @Property    {PreviewSize}        sizes              Size of preview image
 */

/**
 * @RESTModel PreviewLink
 *
 * @Required    [{previewFile}]
 * @Property    {string}            {previewFile}      URL for named preview file
 */

/**
 * @RESTModel PreviewList
 *
 * @Required    [files,signature]
 * @Property    {PreviewFile[]}     files          Files for content preview
 * @Property    {Signature}         signature      Credentials to access content
 */

/**
 * @RESTModel PreviewMetadata
 *
 * @Required    []
 * @Property    {number}     pageCount          Number of pages in document preview
 */

/**
 * @RESTModel Previews
 *
 * @Required    []
 * @Property    {string}     cssScopeClass      CSS class added to stylesheets to scope rules to a particular document preview
 * @Property    {string}     largeUri           URI for large image of content
 * @Property    {string}     mediumUri          URI for medium-sized image of content
 * @Property    {number}     pageCount          Number of pages in document preview
 * @Property    {string}     smallUri           URI for small image of content
 * @Property    {string}     status             Status of preview processing of content         [done,error,ignored,pending]
 * @Property    {number}     total              Number of properties defined for the content's file data
 * @Property    {string}     thumbnailUri       URI for thumbnail image of content
 * @Property    {string}     wideUri            URI for wide-screen image of content
 */

/**
 * @RESTModel PreviewSize
 *
 * @Required    [{previewFile}]
 * @Property    {number}        {previewFile}      Size of named preview file
 */

/**
 * @RESTModel Revision
 *
 * @Required [contentId,created,createdBy,opts,previews,previewsId,revisionId]
 * @Property    {string}        contentId       Unique content ID of the content item
 * @Property    {number}        created         The timestamp that this revision was created (epoch format)
 * @Property    {User}          createdBy       User who created this revision
 * @Property    {string}        filename        File name for uploaded file
 * @Property    {string}        largeUrl        URI for large image of revision
 * @Property    {string}        mediumUrl       URI for medium-sized image of revision
 * @Property    {string}        mime            MIME type for content (e.g. "text/plain")
 * @Property    {Previews}      previews        Preview information for revision
 * @Property    {string}        previewsId      Storage directory of the previews of the revision, within the content item
 * @Property    {string}        revisionId      The ID of the revision
 * @Property    {number}        size            Size of file
 * @Property    {string}        smallUrl        URI for small image of revision
 * @Property    {string}        thumbnailUrl    URI for thumbnail image of revision
 * @Property    {string}        uri             URI for revision
 * @Property    {string}        wideUri         URI for wide-screen preview image of revision
 */

/**
 * @RESTModel RevisionsResponse
 *
 * @Required    [nextToken,results]
 * @Property    {string}        nextToken       Marker to allow continuation of list (empty if list has been exhausted)
 * @Property    {Revision[]}    results         Array of revision information objects
 */

/**
 * @RESTModel Signature
 *
 * @Required    [expires, signature]
 * @Property    {number}        expires                 Time current access signature expires (epoch format)
 * @Property    {string}        signature               Access control signature
 */

/**
 * @RESTModel UpdatedContent
 *
 * @Required    []
 * @Property    {string}        description         Description of content item
 * @Property    {string}        displayName         Name to display for content item
 */
<commit_msg>mover permisssion model to principals<commit_after>/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community 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://opensource.org/licenses/ECL-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.
 */

/**
 * @RESTModel BasicContent
 *
 * @Required    [created,createdBy,displayName,id,lastModified,latestRevisionId,previews,resourceSubType,tenantAlias,visibility]
 * @Property    {number}    created             Timestamp at which this content item was created in milliseconds since epoch
 * @Property    {string}    createdBy           Unique identifier of the user who created this content item
 * @Property    {string}    description         Description of the content item
 * @Property    {string}    displayName         Title of the content item
 * @Property    {string}    id                  Globally unique content ID for this content item
 * @Property    {number}    lastModified        Timestamp at which this content item was modified for the last time in milliseconds since epoch
 * @Property    {string}    latestRevisionId    Unique identifier of the latest revision associated with this piece of content
 * @Property    {Previews}  previews            Preview data of the latest revision of the content item
 * @Property    {string}    resourceSubType     Type of content item                   [collabdoc,file,link]
 * @Property    {string}    tenantAlias         Alias of the tenant this content item is being created in
 * @Property    {string}    visibility          Visibility of the content item         [loggedin,private,public]
 */

/**
 * @RESTModel CollabDocInfo
 *
 * @Required    [url]
 * @Property    {string}    url                 Internal URL to access collaborative document
 */

/**
 * @RESTModel Content
 *
 * @Required    [canShare,created,createdBy,description,displayName,id,isManager,lastModified,latestRevisionId,previews,profilePath,resourceSubType,resourceType,signature,tenant,visibility]
 * @Property    {boolean}       canShare           Can current user share the content
 * @Property    {number}        created            Timestamp at which this content item was created in milliseconds since epoch
 * @Property    {BasicUser}     createdBy          User who created this content item
 * @Property    {string}        description        Description of the content item
 * @Property    {string}        displayName        Title of the content item
 * @Property    {string}        downloadPath       Relative URL to download content source
 * @Property    {string}        etherpadGroupId    Collaborative document group
 * @Property    {string}        etherpadPadId      Collaborative document id
 * @Property    {string}        filename           Name of content file
 * @Property    {string}        id                 Globally unique content ID for this content item
 * @Property    {boolean}       isManager          Is current user a manager of the content
 * @Property    {number}        lastModified       Timestamp at which this content item was modified for the last time in milliseconds since epoch
 * @Property    {Revision}      latestRevision     Information about latest revision of content item
 * @Property    {String}        latestRevisionId   Unique identifier of the latest revision associated with this piece of content
 * @Property    {string}        mime               MIME type for content (e.g. "text/plain")
 * @Property    {Previews}      previews           Preview data of the latest revision of the content item
 * @Property    {string}        profilePath        Relative URL for content's main page
 * @Property    {string}        resourceSubType    Type of content item                   [collabdoc,file,link]
 * @Property    {string}        resourceType       Type of resource                       [content]
 * @Property    {Signature}     signature          Credentials to access content
 * @Property    {number}        size               Size of content file in bytes
 * @Property    {BasicTenant}   tenant             Information about the tenant this content item is being created in
 * @Property    {string}        visibility         Visibility of the content item         [loggedin,private,public]
 */

/**
 * @RESTModel ContentList
 *
 * @Required    [nextToken,results]
 * @Property    {Content[]}     results                 Array of content items associated with object
 * @Property    {string}        nextToken               Marker to allow continuation of search (empty if search has been exhausted)
 */

/**
 * @RESTModel PreviewFile
 *
 * @Required    [filename,size,uri]
 * @Property    {string}            filename            Name of preview file
 * @Property    {string}            size                Size of preview file        [large,medium,small,thumbnail,wide]
 * @Property    {string}            uri                 URI of preview file
 */

/**
 * @RESTModel PreviewInfo
 *
 * @Required    [contentMetadata,links,previewMetadata,sizes]
 * @Property    {UpdatedContent}     contentMetadata    Updated information about content
 * @Property    {PreviewLink}        links              External link to use as preview image
 * @Property    {PreviewMetadata}    previewMetadata    Updated information about preview
 * @Property    {PreviewSize}        sizes              Size of preview image
 */

/**
 * @RESTModel PreviewLink
 *
 * @Required    [{previewFile}]
 * @Property    {string}            {previewFile}      URL for named preview file
 */

/**
 * @RESTModel PreviewList
 *
 * @Required    [files,signature]
 * @Property    {PreviewFile[]}     files          Files for content preview
 * @Property    {Signature}         signature      Credentials to access content
 */

/**
 * @RESTModel PreviewMetadata
 *
 * @Required    []
 * @Property    {number}     pageCount          Number of pages in document preview
 */

/**
 * @RESTModel Previews
 *
 * @Required    []
 * @Property    {string}     cssScopeClass      CSS class added to stylesheets to scope rules to a particular document preview
 * @Property    {string}     largeUri           URI for large image of content
 * @Property    {string}     mediumUri          URI for medium-sized image of content
 * @Property    {number}     pageCount          Number of pages in document preview
 * @Property    {string}     smallUri           URI for small image of content
 * @Property    {string}     status             Status of preview processing of content         [done,error,ignored,pending]
 * @Property    {number}     total              Number of properties defined for the content's file data
 * @Property    {string}     thumbnailUri       URI for thumbnail image of content
 * @Property    {string}     wideUri            URI for wide-screen image of content
 */

/**
 * @RESTModel PreviewSize
 *
 * @Required    [{previewFile}]
 * @Property    {number}        {previewFile}      Size of named preview file
 */

/**
 * @RESTModel Revision
 *
 * @Required [contentId,created,createdBy,opts,previews,previewsId,revisionId]
 * @Property    {string}        contentId       Unique content ID of the content item
 * @Property    {number}        created         The timestamp that this revision was created (epoch format)
 * @Property    {User}          createdBy       User who created this revision
 * @Property    {string}        filename        File name for uploaded file
 * @Property    {string}        largeUrl        URI for large image of revision
 * @Property    {string}        mediumUrl       URI for medium-sized image of revision
 * @Property    {string}        mime            MIME type for content (e.g. "text/plain")
 * @Property    {Previews}      previews        Preview information for revision
 * @Property    {string}        previewsId      Storage directory of the previews of the revision, within the content item
 * @Property    {string}        revisionId      The ID of the revision
 * @Property    {number}        size            Size of file
 * @Property    {string}        smallUrl        URI for small image of revision
 * @Property    {string}        thumbnailUrl    URI for thumbnail image of revision
 * @Property    {string}        uri             URI for revision
 * @Property    {string}        wideUri         URI for wide-screen preview image of revision
 */

/**
 * @RESTModel RevisionsResponse
 *
 * @Required    [nextToken,results]
 * @Property    {string}        nextToken       Marker to allow continuation of list (empty if list has been exhausted)
 * @Property    {Revision[]}    results         Array of revision information objects
 */

/**
 * @RESTModel Signature
 *
 * @Required    [expires, signature]
 * @Property    {number}        expires                 Time current access signature expires (epoch format)
 * @Property    {string}        signature               Access control signature
 */

/**
 * @RESTModel UpdatedContent
 *
 * @Required    []
 * @Property    {string}        description         Description of content item
 * @Property    {string}        displayName         Name to display for content item
 */
<|endoftext|>


<commit_before>// Test borrowed from ember-cli-mirage

var expect = require('chai').expect;
var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

function getAddon() {
  var app = new EmberAddon();
  var addons = app.project.addons;

  for(var i = 0; i < addons.length; i++) {
    if(addons[i].name === 'ember-cli-page-object') {
      return addons[i];
    }
  }
}

describe('Addon', function() {
  this.timeout(15000);

  afterEach(function() {
    delete process.env.EMBER_ENV;
  });

  function treeForTests(name) {
    it('returns an empty tree in production environment by default', function() {
      process.env.EMBER_ENV = 'production';
      var addonTree = getAddon().treeFor(name);

      expect(addonTree).to.be.undefined;
    });

    ['development', 'test'].forEach(function(environment) {
      it('returns a tree in ' + environment + ' environment by default', function() {
        process.env.EMBER_ENV = environment;
        var addonTree = getAddon().treeFor(name);

        expect(addonTree._inputNodes.length).to.not.equal(0);
      });
    });
  }

  describe('#treeFor addon', function() {
    treeForTests('addon');
  });

  describe('#treeFor app', function() {
    treeForTests('app');
  });
});
<commit_msg>Remove addon-tree-test.js<commit_after><|endoftext|>


<commit_before>
var Operator = require("dice-js/operator");

Operator.define("Addition", {
    symbol: "+",
    operation: function (lVal, rVal) {
        var lValIsArray = Array.isArray(lVal),
            rValIsArray = Array.isArray(rVal),
            lValIsObj   = typeof lVal === "object",
            rValIsObj   = typeof rVal === "object"
        ;

        if (lValIsArray) {
            if (rValIsArray) {
                return lVal.concat(rVal);
            }
            else {
                lVal.push(rVal);
            }
        }
        else if (lValIsObj) {
            if (rValIsArray || !rValIsObj) {
                return [lVal, rVal];
            }
            else {
                return Object.keys(rVal).reduce(function (acc, key) {
                    if (acc[key] !== undefined) {
                        acc[key] += rVal[key];
                    }
                    else {
                        acc[key] = rVal[key];
                    }
                    return acc;
                }, Object.keys(lVal).reduce(function (acc, key) {
                    acc[key] = lVal[key];
                    return acc;
                }, {}));
            }
        }
        else {
            return lVal + rVal;
        }
    },
    precedence: 0,
    associativity: "left"
});
<commit_msg>Increase each item of the lVal by rVal when lVal is an array and rVal is a plain value.<commit_after>
var Operator = require("dice-js/operator");

Operator.define("Addition", {
    symbol: "+",
    operation: function (lVal, rVal) {
        var lValIsArray = Array.isArray(lVal),
            rValIsArray = Array.isArray(rVal),
            lValIsObj   = typeof lVal === "object",
            rValIsObj   = typeof rVal === "object"
        ;

        if (lValIsArray) {
            if (rValIsArray) {
                // is this the correct default?
                // should we add values from rVal to the corresponding values in lVal?
                return lVal.concat(rVal);
            }
            else if (rValIsObj) {
                return [lVal, rVal];
            }
            else {
                return lVal.map(function (val) {
                    return val + rVal;
                });
            }
        }
        else if (lValIsObj) {
            if (rValIsArray || !rValIsObj) {
                return [lVal, rVal];
            }
            else {
                return Object.keys(rVal).reduce(function (acc, key) {
                    if (acc[key] !== undefined) {
                        acc[key] += rVal[key];
                    }
                    else {
                        acc[key] = rVal[key];
                    }
                    return acc;
                }, Object.keys(lVal).reduce(function (acc, key) {
                    acc[key] = lVal[key];
                    return acc;
                }, {}));
            }
        }
        else {
            return lVal + rVal;
        }
    },
    precedence: 0,
    associativity: "left"
});
<|endoftext|>


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

    const { get } = require('ts-utils');

    const searchByNameAndId = ($scope, key, list) => {
        const query = $scope[key];
        if (!query) {
            return list;
        }

        return list.filter((item) => {
            const name = get({ item }, 'item.asset.name');
            const id = get({ item }, 'item.asset.id');
            return String(name).toLowerCase().indexOf(query.toLowerCase()) !== -1 || String(id) === query;
        });
    };

    const ds = require('data-service');

    /**
     * @param {Base} Base
     * @param {$rootScope.Scope} $scope
     * @param {Waves} waves
     * @param {app.utils} utils
     * @param {ModalManager} modalManager
     * @param {User} user
     * @param {EventManager} eventManager
     * @param {GatewayService} gatewayService
     * @param {$state} $state
     * @param {STService} stService
     * @param {VisibleService} visibleService
     * @param {BalanceWatcher} balanceWatcher
     * @return {PortfolioCtrl}
     */
    const controller = function (Base, $scope, waves, utils, modalManager, user,
                                 eventManager, gatewayService, $state,
                                 stService, visibleService, balanceWatcher) {

        class PortfolioCtrl extends Base {

            constructor() {
                super($scope);
                /**
                 * @type {string}
                 */
                this.mirrorId = user.getSetting('baseAssetId');
                /**
                 * @type {Asset}
                 */
                this.mirror = null;
                /**
                 * @type {string[]}
                 */
                this.pinned = [];
                /**
                 * @type {string}
                 */
                this.address = user.address;
                /**
                 * @type {Array<string>}
                 */
                this.spam = [];
                /**
                 * @type {PortfolioCtrl.IBalances}
                 */
                this.details = null;
                /**
                 * @type {Array<PortfolioCtrl.IPortfolioBalanceDetails>}
                 */
                this.balanceList = [];
                /**
                 * @type {string}
                 */
                this.filter = null;
                /**
                 * @type {Moment}
                 */
                this.chartStartDate = utils.moment().add().day(-7);
                /**
                 * @type {boolean}
                 */
                this.pending = true;

                waves.node.assets.getAsset(this.mirrorId)
                    .then((mirror) => {
                        this.mirror = mirror;
                        /**
                         * @type {Array<SmartTable.IHeaderInfo>}
                         */
                        this.tableHeaders = [
                            {
                                id: 'name',
                                title: { literal: 'list.name' },
                                valuePath: 'item.asset.name',
                                sort: true,
                                search: searchByNameAndId,
                                placeholder: 'portfolio.filter'
                            },
                            {
                                id: 'balance',
                                title: { literal: 'list.balance' },
                                valuePath: 'item.available',
                                sort: true
                            },
                            {
                                id: 'inOrders',
                                title: { literal: 'list.inOrders' },
                                valuePath: 'item.inOrders',
                                sort: true
                            },
                            {
                                id: 'mirror',
                                title: { literal: 'list.mirror', params: { currency: mirror.displayName } }
                            },
                            {
                                id: 'rate',
                                title: { literal: 'list.rate', params: { currency: mirror.displayName } }
                            },
                            {
                                id: 'change24',
                                title: { literal: 'list.change' }
                            },
                            {
                                id: 'controls'
                            }
                        ];

                        $scope.$digest();
                    });

                this.syncSettings({
                    pinned: 'pinnedAssetIdList',
                    spam: 'wallet.portfolio.spam',
                    filter: 'wallet.portfolio.filter'
                });

                balanceWatcher.ready
                    .then(() => {
                        this.receive(balanceWatcher.change, this._updateBalances, this);
                        this.observe(['pinned', 'spam'], this._updateBalances);
                        this._updateBalances();
                    });

                balanceWatcher.ready.then(() => {
                    this.pending = false;
                    this.observe('details', this._onChangeDetails);
                    this.observe('filter', this._onChangeDetails);

                    this._onChangeDetails();
                });

                this.receive(stService.sort, () => {
                    visibleService.updateSort();
                });
            }

            /**
             * @param {Asset} asset
             */
            showAsset(asset) {
                modalManager.showAssetInfo(asset);
            }

            /**
             * @param {Asset} asset
             */
            showSend(asset) {
                return modalManager.showSendAsset({ assetId: asset && asset.id });
            }

            /**
             * @param {Asset} asset
             */
            showReceivePopup(asset) {
                return modalManager.showReceiveModal(user, asset);
            }

            /**
             * @param {Asset} asset
             */
            showDeposit(asset) {
                return modalManager.showDepositAsset(user, asset);
            }

            /**
             * @param {Asset} asset
             */
            showSepa(asset) {
                return modalManager.showSepaAsset(user, asset);
            }

            showQR() {
                return modalManager.showAddressQrCode(user);
            }

            showBurn(assetId) {
                return modalManager.showBurnModal(assetId);
            }

            showReissue(assetId) {
                return modalManager.showReissueModal(assetId);
            }

            canShowDex(balance) {
                return balance.isPinned ||
                    balance.asset.isMyAsset ||
                    balance.asset.id === WavesApp.defaultAssets.WAVES ||
                    gatewayService.getPurchasableWithCards()[balance.asset.id] ||
                    gatewayService.getCryptocurrencies()[balance.asset.id] ||
                    gatewayService.getFiats()[balance.asset.id];
            }

            /**
             * @param {Asset} asset
             */
            openDex(asset) {
                $state.go('main.dex', this.getSrefParams(asset));
            }

            /**
             * @param {Asset} asset
             */
            getSrefParams(asset) {
                utils.openDex(asset.id);
            }

            /**
             * @param {Asset} asset
             * @param {boolean} [state]
             */
            togglePin(asset, state) {
                user.togglePinAsset(asset.id, state);
                this.poll.restart();
            }

            /**
             * @param {Asset} asset
             * @param {boolean} [state]
             */
            toggleSpam(asset, state) {
                user.toggleSpamAsset(asset.id, state);
                this.poll.restart();
            }

            isDepositSupported(asset) {
                const isWaves = asset.id === WavesApp.defaultAssets.WAVES;

                return gatewayService.hasSupportOf(asset, 'deposit') || isWaves;
            }

            isSepaSupported(asset) {
                return gatewayService.hasSupportOf(asset, 'sepa');
            }

            /**
             * @private
             */
            _onChangeDetails() {
                const details = this.details;
                let balanceList;

                switch (this.filter) {
                    case 'active':
                        balanceList = details.active.slice();
                        break;
                    case 'pinned':
                        balanceList = details.pinned.slice();
                        break;
                    case 'spam':
                        balanceList = details.spam.slice();
                        break;
                    case 'my':
                        balanceList = details.my.slice();
                        break;
                    case 'verified':
                        balanceList = details.verified.slice();
                        break;
                    default:
                        throw new Error('Wrong filter name!');
                }

                this.balanceList = balanceList;
            }

            /**
             * @private
             */
            _updateBalances() {
                const details = balanceWatcher.getFullBalanceList()
                    .map(item => {
                        const isPinned = this._isPinned(item.asset.id);
                        const isSpam = this._isSpam(item.asset.id);
                        const isOnScamList = WavesApp.scam[item.asset.id];

                        return {
                            available: item.available,
                            asset: item.asset,
                            inOrders: item.inOrders,
                            isPinned,
                            isSpam,
                            isOnScamList,
                            minSponsoredAssetFee: item.asset.minSponsoredAssetFee,
                            sponsorBalance: item.asset.sponsorBalance
                        };
                    })
                    .reduce((acc, item) => {
                        const oracleData = ds.dataManager.getOracleAssetData(item.asset.id);

                        if (item.asset.sender === user.address) {
                            acc.my.push(item);
                        }
                        if (oracleData && oracleData.status > 0) {
                            acc.verified.push(item);
                        }
                        if (item.isOnScamList || item.isSpam) {
                            acc.spam.push(item);
                        } else {
                            acc.active.push(item);
                        }

                        return acc;
                    }, { spam: [], my: [], active: [], verified: [] });

                this.details = details;
                utils.safeApply($scope);
            }

            /**
             * @param assetId
             * @return {boolean}
             * @private
             */
            _isPinned(assetId) {
                return this.pinned.includes(assetId);
            }

            /**
             * @param assetId
             * @return {boolean}
             * @private
             */
            _isSpam(assetId) {
                return this.spam.includes(assetId);
            }

        }

        return new PortfolioCtrl();
    };

    controller.$inject = [
        'Base',
        '$scope',
        'waves',
        'utils',
        'modalManager',
        'user',
        'eventManager',
        'gatewayService',
        '$state',
        'stService',
        'visibleService',
        'balanceWatcher'
    ];

    angular.module('app.wallet.portfolio')
        .controller('PortfolioCtrl', controller);
})();

/**
 * @name PortfolioCtrl
 */

/**
 * @typedef {object} PortfolioCtrl#IPortfolioBalanceDetails
 * @property {boolean} isPinned
 * @property {boolean} isSpam
 * @property {boolean} isOnScamList
 * @property {Asset} asset
 * @property {Money} available
 * @property {Money} inOrders
 * @property {Money|void} minSponsoredAssetFee
 * @property {Money|void} sponsorBalance
 */

/**
 * @typedef {object} PortfolioCtrl#IBalances
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} active
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} pinned // TODO when available assets store
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} spam
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} my
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} verified
 */
<commit_msg>CLIENT-1716: fix portfolio visible<commit_after>(function () {
    'use strict';

    const { get } = require('ts-utils');

    const searchByNameAndId = ($scope, key, list) => {
        const query = $scope[key];
        if (!query) {
            return list;
        }

        return list.filter((item) => {
            const name = get({ item }, 'item.asset.name');
            const id = get({ item }, 'item.asset.id');
            return String(name).toLowerCase().indexOf(query.toLowerCase()) !== -1 || String(id) === query;
        });
    };

    const ds = require('data-service');

    /**
     * @param {Base} Base
     * @param {$rootScope.Scope} $scope
     * @param {Waves} waves
     * @param {app.utils} utils
     * @param {ModalManager} modalManager
     * @param {User} user
     * @param {EventManager} eventManager
     * @param {GatewayService} gatewayService
     * @param {$state} $state
     * @param {STService} stService
     * @param {VisibleService} visibleService
     * @param {BalanceWatcher} balanceWatcher
     * @return {PortfolioCtrl}
     */
    const controller = function (Base, $scope, waves, utils, modalManager, user,
                                 eventManager, gatewayService, $state,
                                 stService, visibleService, balanceWatcher) {

        class PortfolioCtrl extends Base {

            constructor() {
                super($scope);
                /**
                 * @type {string}
                 */
                this.mirrorId = user.getSetting('baseAssetId');
                /**
                 * @type {Asset}
                 */
                this.mirror = null;
                /**
                 * @type {string[]}
                 */
                this.pinned = [];
                /**
                 * @type {string}
                 */
                this.address = user.address;
                /**
                 * @type {Array<string>}
                 */
                this.spam = [];
                /**
                 * @type {PortfolioCtrl.IBalances}
                 */
                this.details = null;
                /**
                 * @type {Array<PortfolioCtrl.IPortfolioBalanceDetails>}
                 */
                this.balanceList = [];
                /**
                 * @type {string}
                 */
                this.filter = null;
                /**
                 * @type {Moment}
                 */
                this.chartStartDate = utils.moment().add().day(-7);
                /**
                 * @type {boolean}
                 */
                this.pending = true;

                waves.node.assets.getAsset(this.mirrorId)
                    .then((mirror) => {
                        this.mirror = mirror;
                        /**
                         * @type {Array<SmartTable.IHeaderInfo>}
                         */
                        this.tableHeaders = [
                            {
                                id: 'name',
                                title: { literal: 'list.name' },
                                valuePath: 'item.asset.name',
                                sort: true,
                                search: searchByNameAndId,
                                placeholder: 'portfolio.filter'
                            },
                            {
                                id: 'balance',
                                title: { literal: 'list.balance' },
                                valuePath: 'item.available',
                                sort: true
                            },
                            {
                                id: 'inOrders',
                                title: { literal: 'list.inOrders' },
                                valuePath: 'item.inOrders',
                                sort: true
                            },
                            {
                                id: 'mirror',
                                title: { literal: 'list.mirror', params: { currency: mirror.displayName } }
                            },
                            {
                                id: 'rate',
                                title: { literal: 'list.rate', params: { currency: mirror.displayName } }
                            },
                            {
                                id: 'change24',
                                title: { literal: 'list.change' }
                            },
                            {
                                id: 'controls'
                            }
                        ];

                        $scope.$digest();
                    });

                this.syncSettings({
                    pinned: 'pinnedAssetIdList',
                    spam: 'wallet.portfolio.spam',
                    filter: 'wallet.portfolio.filter'
                });

                balanceWatcher.ready
                    .then(() => {
                        this.receive(balanceWatcher.change, this._updateBalances, this);
                        this.observe(['pinned', 'spam'], () => {
                            this._updateBalances();
                            visibleService.updateSort();
                        });
                        this._updateBalances();
                    });

                balanceWatcher.ready.then(() => {
                    this.pending = false;
                    this.observe('details', this._onChangeDetails);
                    this.observe('filter', this._onChangeDetails);

                    this._onChangeDetails();
                });

                this.receive(stService.sort, () => {
                    visibleService.updateSort();
                });
            }

            /**
             * @param {Asset} asset
             */
            showAsset(asset) {
                modalManager.showAssetInfo(asset);
            }

            /**
             * @param {Asset} asset
             */
            showSend(asset) {
                return modalManager.showSendAsset({ assetId: asset && asset.id });
            }

            /**
             * @param {Asset} asset
             */
            showReceivePopup(asset) {
                return modalManager.showReceiveModal(user, asset);
            }

            /**
             * @param {Asset} asset
             */
            showDeposit(asset) {
                return modalManager.showDepositAsset(user, asset);
            }

            /**
             * @param {Asset} asset
             */
            showSepa(asset) {
                return modalManager.showSepaAsset(user, asset);
            }

            showQR() {
                return modalManager.showAddressQrCode(user);
            }

            showBurn(assetId) {
                return modalManager.showBurnModal(assetId);
            }

            showReissue(assetId) {
                return modalManager.showReissueModal(assetId);
            }

            canShowDex(balance) {
                return balance.isPinned ||
                    balance.asset.isMyAsset ||
                    balance.asset.id === WavesApp.defaultAssets.WAVES ||
                    gatewayService.getPurchasableWithCards()[balance.asset.id] ||
                    gatewayService.getCryptocurrencies()[balance.asset.id] ||
                    gatewayService.getFiats()[balance.asset.id];
            }

            /**
             * @param {Asset} asset
             */
            openDex(asset) {
                $state.go('main.dex', this.getSrefParams(asset));
            }

            /**
             * @param {Asset} asset
             */
            getSrefParams(asset) {
                utils.openDex(asset.id);
            }

            /**
             * @param {Asset} asset
             * @param {boolean} [state]
             */
            togglePin(asset, state) {
                user.togglePinAsset(asset.id, state);
                this.poll.restart();
            }

            /**
             * @param {Asset} asset
             * @param {boolean} [state]
             */
            toggleSpam(asset, state) {
                user.toggleSpamAsset(asset.id, state);
                this.poll.restart();
            }

            isDepositSupported(asset) {
                const isWaves = asset.id === WavesApp.defaultAssets.WAVES;

                return gatewayService.hasSupportOf(asset, 'deposit') || isWaves;
            }

            isSepaSupported(asset) {
                return gatewayService.hasSupportOf(asset, 'sepa');
            }

            /**
             * @private
             */
            _onChangeDetails() {
                const details = this.details;
                let balanceList;

                switch (this.filter) {
                    case 'active':
                        balanceList = details.active.slice();
                        break;
                    case 'pinned':
                        balanceList = details.pinned.slice();
                        break;
                    case 'spam':
                        balanceList = details.spam.slice();
                        break;
                    case 'my':
                        balanceList = details.my.slice();
                        break;
                    case 'verified':
                        balanceList = details.verified.slice();
                        break;
                    default:
                        throw new Error('Wrong filter name!');
                }

                this.balanceList = balanceList;
            }

            /**
             * @private
             */
            _updateBalances() {
                const details = balanceWatcher.getFullBalanceList()
                    .map(item => {
                        const isPinned = this._isPinned(item.asset.id);
                        const isSpam = this._isSpam(item.asset.id);
                        const isOnScamList = WavesApp.scam[item.asset.id];

                        return {
                            available: item.available,
                            asset: item.asset,
                            inOrders: item.inOrders,
                            isPinned,
                            isSpam,
                            isOnScamList,
                            minSponsoredAssetFee: item.asset.minSponsoredAssetFee,
                            sponsorBalance: item.asset.sponsorBalance
                        };
                    })
                    .reduce((acc, item) => {
                        const oracleData = ds.dataManager.getOracleAssetData(item.asset.id);

                        if (item.asset.sender === user.address) {
                            acc.my.push(item);
                        }
                        if (oracleData && oracleData.status > 0) {
                            acc.verified.push(item);
                        }
                        if (item.isOnScamList || item.isSpam) {
                            acc.spam.push(item);
                        } else {
                            acc.active.push(item);
                        }

                        return acc;
                    }, { spam: [], my: [], active: [], verified: [] });

                this.details = details;
                utils.safeApply($scope);
            }

            /**
             * @param assetId
             * @return {boolean}
             * @private
             */
            _isPinned(assetId) {
                return this.pinned.includes(assetId);
            }

            /**
             * @param assetId
             * @return {boolean}
             * @private
             */
            _isSpam(assetId) {
                return this.spam.includes(assetId);
            }

        }

        return new PortfolioCtrl();
    };

    controller.$inject = [
        'Base',
        '$scope',
        'waves',
        'utils',
        'modalManager',
        'user',
        'eventManager',
        'gatewayService',
        '$state',
        'stService',
        'visibleService',
        'balanceWatcher'
    ];

    angular.module('app.wallet.portfolio')
        .controller('PortfolioCtrl', controller);
})();

/**
 * @name PortfolioCtrl
 */

/**
 * @typedef {object} PortfolioCtrl#IPortfolioBalanceDetails
 * @property {boolean} isPinned
 * @property {boolean} isSpam
 * @property {boolean} isOnScamList
 * @property {Asset} asset
 * @property {Money} available
 * @property {Money} inOrders
 * @property {Money|void} minSponsoredAssetFee
 * @property {Money|void} sponsorBalance
 */

/**
 * @typedef {object} PortfolioCtrl#IBalances
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} active
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} pinned // TODO when available assets store
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} spam
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} my
 * @property {Array<PortfolioCtrl.IPortfolioBalanceDetails>} verified
 */
<|endoftext|>