<commit_before>'use strict';

const evejsapi = require('evejsapi');
const _ = require('lodash');

const EveCentralClient = new evejsapi.client.evecentral({
  cache: new evejsapi.cache.redis()
});

const RedisCache = require('./cache/redis');
const cache = new RedisCache();

class EveCentral {

  constructor() {
    /**
     * @todo in Config auslagern
     */
    this.imageServer = 'https://image.eveonline.com';
    this.eveCentralUrl = 'https://eve-central.com/home/quicklook.html?';

    this.validPricetypes = ['sell', 'buy'];

    this.cacheNames = {
      hub: 'central:hub:',
      price: 'central:price:'
    };

    this.cacheTimes = {
      hub: 2592000, // 30 days
      price: 2592000 // 30 days
    };
  }

  hub(bot, message) {
    bot.startTyping(message);
    const self = this;

    self.getSavedHub(message.user)
      .then((savedHub) => {
        const hubInfo = message.text.split(/ /);
        if (hubInfo.length === 2) {
          return savedHub;
        } else {
          return self.saveHub(message.user, hubInfo[hubInfo.length - 1]);
        }
      })
      .then((savedHub) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'good',
            title: 'saved Hub: ' + savedHub
          }, EveCentral.hubHelp()]
        });
      })
      .catch((err) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'danger',
            title: 'save failed: ' + err
          }, EveCentral.hubHelp()]
        });
      });
  }

  getSavedHub(user) {
    return new Promise((resolve, reject) => {
      const cacheKey = this.cacheNames['hub'] + user;
      cache.read(cacheKey)
        .then((hub) => {
          if (hub !== null) {
            resolve(_.invert(EveCentral.tradeHubs())[hub]);
          } else {
            resolve('none');
          }
        })
        .catch(reject);
    });
  }

  saveHub(user, hub) {
    return new Promise((resolve, reject) => {
      hub = hub.toLowerCase();

      if (this._isValidHub(hub)) {
        const cacheKey = this.cacheNames['hub'] + user;
        cache.write(cacheKey, EveCentral.tradeHubs()[hub], this.cacheTimes['hub'])
          .then(() => {
            resolve(hub);
          })
          .catch(reject);
      } else {
        resolve('none');
      }
    });
  }

  pricetype(bot, message) {
    bot.startTyping(message);

    const self = this;

    self.getSavedPricetype(message.user)
      .then((savedPricetype) => {
        const priceInfo = message.text.split(/ /);
        if (priceInfo.length === 2) {
          return savedPricetype;
        } else {
          return self.savePricetype(message.user, priceInfo[priceInfo.length - 1]);
        }
      })
      .then((savedPricetype) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'good',
            title: 'saved Price: ' + savedPricetype
          }, EveCentral.pricetypeHelp()]
        });
      })
      .catch((err) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'danger',
            title: 'saved Price failed: ' + err
          }, EveCentral.pricetypeHelp()]
        });
      });

  }

  getSavedPricetype(user) {
    return new Promise((resolve) => {
      const cacheKey = this.cacheNames['price'] + user;
      cache.read(cacheKey)
        .then((hub) => {
          resolve(hub || 'none');
        });
    });
  }

  savePricetype(user, pricetype) {
    return new Promise((resolve) => {
      pricetype = pricetype.toLowerCase();

      if (this._isValidPricetype(pricetype)) {
        const cacheKey = this.cacheNames['price'] + user;
        cache.write(cacheKey, pricetype, this.cacheTimes['price'])
          .then(() => {
            resolve(pricetype);
          });
      } else {
        resolve('none');
      }
    });
  }

  _isValidHub(hub) {
    const tradeHubs = EveCentral.tradeHubs();
    return _.has(tradeHubs, hub);
  }

  _isValidPricetype(pricetype) {
    return (_.indexOf(this.validPricetypes, pricetype) !== -1);
  }

  fetch(bot, message, data, sqliteDb) {
    bot.startTyping(message);

    const self = this;
    let hub, hubHumanReadable, pricetype, searchItem;

    Promise.resolve()
      .then(() => {
        if (_.isString(data[2]) && this._isValidPricetype(data[2])) {
          return data[2];
        } else if (_.isString(data[2]) && !this._isValidPricetype(data[2])) {
          throw new Error('pricetype is not valid');
        } else {
          return self.getSavedPricetype(message.user)
            .then((_pricetype) => {
              if (_pricetype === 'none') {
                throw new Error('no saved priceType found!');
              }

              return _pricetype;
            });
        }
      })
      .then((_pricetype) => {
        pricetype = _pricetype;
        if (_.isString(data[3])) {
          data[3] = data[3].toLowerCase();
        }

        if (_.isString(data[3]) && this._isValidHub(data[3])) {
          hubHumanReadable = data[3];
          return EveCentral.tradeHubs()[data[3]];
        } else if (_.isString(data[3]) && !this._isValidHub(data[3])) {
          throw new Error('hub is not valid');
        } else {
          return self.getSavedHub(message.user)
            .then((_savedhub) => {
              if (_savedhub === 'none') {
                throw new Error('no saved hub found!');
              }

              hubHumanReadable = _savedhub;

              return EveCentral.tradeHubs()[_savedhub];
            });
        }
      })
      .then((_hub) => {
        hub = _hub;
        searchItem = data[1].replace(/"/g, '');

        return self._fetchDb(sqliteDb, searchItem);
      })
      .then((rows) => {

        let request = [];
        _.forEach(rows, (row) => {
          request.push(['typeid', row.typeID]);
        });

        request.push(['usesystem', hub]);
        EveCentralClient.fetch('marketstat', request)
          .then((data) => {

            const marketStat = data.marketstat.type[pricetype];
            const min = parseFloat(marketStat.min);
            const max = parseFloat(marketStat.max);
            const typeId = data.marketstat.type.id;

            bot.reply(message, {
              text: '*Eve-Central ' + hubHumanReadable.capitalize() + ' ' + pricetype.capitalize() + '*',
              attachments: [{
                color: 'good',
                text: '*<' + self.eveCentralUrl + 'typeid=' + typeId + '&usesystem=' + hub + '#sells|' + rows[0].typeName.capitalize() + '>' +
                '*\n_*min*  `' + min.formatISK(2, 3, '.', ',') + ' ISK`_ \n_*max*  `' + max.formatISK(2, 3, '.', ',') + ' ISK`_ \n',
                thumb_url: self.imageServer + '/Type/' + typeId + '_64.png',
                mrkdwn_in: [
                  'text'
                ]
              }],
              mrkdwn_in: [
                'text'
              ]
            });
          });
      })
      .catch((err) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'danger',
            title: 'fetch failed: ' + err
          }]
        });
      });
  }

  _fetchDb(sqliteDb, item) {
    return new Promise((resolve, reject) => {
      sqliteDb.all('SELECT typeID, typeName FROM invTypes WHERE typeName = "' + item + '" COLLATE NOCASE', (err, rows) => {
        if (err) {
          return reject(err);
        }

        if (rows.length === 0) {
          return reject('Item not found');
        }

        resolve(rows);
      });
    });
  }

  static tradeHubs() {
    return {
      jita: '30000142',
      hek: '30002053',
      amarr: '30002187',
      rens: '30002510',
      dodixie: '30002659'
    };
  }

  static globalHelp() {
    return '• [!central] -- command that can return the price of an item or a predefined group\n' +
      '• [!industry] -- command that ca return all actual industry jobs\n' +
      '• [!industry-history] [lastMonth|lastWeek|last2Weeks] -- command tha can return all complete industry jobs';
  }

  static help() {
    return {
      color: 'good',
      title: '!central',
      pretext: 'EvE Central Bot Help',
      text: 'this help'
    };
  }

  static hubHelp() {
    return {
      title: '!central hub [<hubname>]',
      text: 'should return the actual tradehub of which the bot should request prices' +
      '\n[<hubname>] can be amarr, jita, dodixie, rens or hek' +
      '\nif <hubname> is entered this tradehub should be set as station for the channel where its entered'
    };
  }

  static pricetypeHelp() {
    return {
      title: '!central price [<pricetype>]',
      text: 'should return the actual pricetype for which the bot should request prices from a hub' +
      '\n[<pricetype>] can either be sell or buy' +
      '\nif <pricetype> is entered this pricetag should be set as pricelist for the channel where its entered'
    };
  }

  static directhelp() {
    return {
      title: '!central "<typename>" [<pricetype>] [<hubname>]',
      text: 'should return the actual price from the eve-central - api.' +
      '\n*"<typename>"* type of good that should be check with the eve-central-api' +
      '\n*must be within quotation marks* so the typename can be validated to the typename/typeid lookuptable.' +
      '\n[<pricetype>] can either be sell or buy' +
      '\n[<hubname>] can be amarr, jita, dodixie, rens or hek' +
      '\nif <pricetype> is entered the api should return the price for the requested pricetag, ignoring the standard pricetag set for this channel.' +
      '\nif <hubname> is entered the api should return the price for the requested hub, ignoring the standard hub set for this channel.',
      mrkdwn_in: [
        'text'
      ]
    };
  }

  static groupHelp() {
    return {
      title: '!central group <groupname> - not implemented yet',
      text: 'should return a grouped pricelist for a special group ' +
      '<groupname> can be ore, cpore, minerals, p1, p2, p3, p4, ... (can be extendend)',
      mrkdwn_in: [
        'text'
      ]
    };
  }

  static allHelp() {
    return [
      EveCentral.help(),
      EveCentral.hubHelp(),
      EveCentral.pricetypeHelp(),
      EveCentral.directhelp(),
      EveCentral.groupHelp()
    ];
  }
}

module.exports = EveCentral;
<commit_msg>Finish v0.4.1<commit_after>'use strict';

const evejsapi = require('evejsapi');
const _ = require('lodash');

const EveCentralClient = new evejsapi.client.evecentral({
  cache: new evejsapi.cache.redis()
});

const RedisCache = require('./cache/redis');
const cache = new RedisCache();

class EveCentral {

  constructor() {
    /**
     * @todo in Config auslagern
     */
    this.imageServer = 'https://image.eveonline.com';
    this.eveCentralUrl = 'https://eve-central.com/home/quicklook.html?';

    this.validPricetypes = ['sell', 'buy'];

    this.cacheNames = {
      hub: 'central:hub:',
      price: 'central:price:'
    };

    this.cacheTimes = {
      hub: 2592000 * 12, // 365 days
      price: 2592000 * 12 // 365 days
    };
  }

  hub(bot, message) {
    bot.startTyping(message);
    const self = this;

    self.getSavedHub(message.user)
      .then((savedHub) => {
        const hubInfo = message.text.split(/ /);
        if (hubInfo.length === 2) {
          return savedHub;
        } else {
          return self.saveHub(message.user, hubInfo[hubInfo.length - 1]);
        }
      })
      .then((savedHub) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'good',
            title: 'saved Hub: ' + savedHub
          }, EveCentral.hubHelp()]
        });
      })
      .catch((err) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'danger',
            title: 'save failed: ' + err
          }, EveCentral.hubHelp()]
        });
      });
  }

  getSavedHub(user) {
    return new Promise((resolve, reject) => {
      const cacheKey = this.cacheNames['hub'] + user;
      cache.read(cacheKey)
        .then((hub) => {
          if (hub !== null) {
            const savedHub = _.invert(EveCentral.tradeHubs())[hub];
            resolve(savedHub);
            this.saveHub(user, savedHub);
          } else {
            resolve('none');
          }
        })
        .catch(reject);
    });
  }

  saveHub(user, hub) {
    return new Promise((resolve, reject) => {
      hub = hub.toLowerCase();

      if (this._isValidHub(hub)) {
        const cacheKey = this.cacheNames['hub'] + user;
        cache.write(cacheKey, EveCentral.tradeHubs()[hub], this.cacheTimes['hub'])
          .then(() => {
            resolve(hub);
          })
          .catch(reject);
      } else {
        resolve('none');
      }
    });
  }

  pricetype(bot, message) {
    bot.startTyping(message);

    const self = this;

    self.getSavedPricetype(message.user)
      .then((savedPricetype) => {
        const priceInfo = message.text.split(/ /);
        if (priceInfo.length === 2) {
          return savedPricetype;
        } else {
          return self.savePricetype(message.user, priceInfo[priceInfo.length - 1]);
        }
      })
      .then((savedPricetype) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'good',
            title: 'saved Price: ' + savedPricetype
          }, EveCentral.pricetypeHelp()]
        });
      })
      .catch((err) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'danger',
            title: 'saved Price failed: ' + err
          }, EveCentral.pricetypeHelp()]
        });
      });

  }

  getSavedPricetype(user) {
    return new Promise((resolve) => {
      const cacheKey = this.cacheNames['price'] + user;
      cache.read(cacheKey)
        .then((price) => {
          resolve(price || 'none');
          this.savePricetype(user, price);
        });
    });
  }

  savePricetype(user, pricetype) {
    return new Promise((resolve) => {
      pricetype = pricetype.toLowerCase();

      if (this._isValidPricetype(pricetype)) {
        const cacheKey = this.cacheNames['price'] + user;
        cache.write(cacheKey, pricetype, this.cacheTimes['price'])
          .then(() => {
            resolve(pricetype);
          });
      } else {
        resolve('none');
      }
    });
  }

  _isValidHub(hub) {
    const tradeHubs = EveCentral.tradeHubs();
    return _.has(tradeHubs, hub);
  }

  _isValidPricetype(pricetype) {
    return (_.indexOf(this.validPricetypes, pricetype) !== -1);
  }

  fetch(bot, message, data, sqliteDb) {
    bot.startTyping(message);

    const self = this;
    let hub, hubHumanReadable, pricetype, searchItem;

    Promise.resolve()
      .then(() => {
        if (_.isString(data[2]) && this._isValidPricetype(data[2])) {
          return data[2];
        } else if (_.isString(data[2]) && !this._isValidPricetype(data[2])) {
          throw new Error('pricetype is not valid');
        } else {
          return self.getSavedPricetype(message.user)
            .then((_pricetype) => {
              if (_pricetype === 'none') {
                throw new Error('no saved priceType found!');
              }

              return _pricetype;
            });
        }
      })
      .then((_pricetype) => {
        pricetype = _pricetype;
        if (_.isString(data[3])) {
          data[3] = data[3].toLowerCase();
        }

        if (_.isString(data[3]) && this._isValidHub(data[3])) {
          hubHumanReadable = data[3];
          return EveCentral.tradeHubs()[data[3]];
        } else if (_.isString(data[3]) && !this._isValidHub(data[3])) {
          throw new Error('hub is not valid');
        } else {
          return self.getSavedHub(message.user)
            .then((_savedhub) => {
              if (_savedhub === 'none') {
                throw new Error('no saved hub found!');
              }

              hubHumanReadable = _savedhub;

              return EveCentral.tradeHubs()[_savedhub];
            });
        }
      })
      .then((_hub) => {
        hub = _hub;
        searchItem = data[1].replace(/"/g, '');

        return self._fetchDb(sqliteDb, searchItem);
      })
      .then((rows) => {

        let request = [];
        _.forEach(rows, (row) => {
          request.push(['typeid', row.typeID]);
        });

        request.push(['usesystem', hub]);
        EveCentralClient.fetch('marketstat', request)
          .then((data) => {

            const marketStat = data.marketstat.type[pricetype];
            const min = parseFloat(marketStat.min);
            const max = parseFloat(marketStat.max);
            const typeId = data.marketstat.type.id;

            bot.reply(message, {
              text: '*Eve-Central ' + hubHumanReadable.capitalize() + ' ' + pricetype.capitalize() + '*',
              attachments: [{
                color: 'good',
                text: '*<' + self.eveCentralUrl + 'typeid=' + typeId + '&usesystem=' + hub + '#sells|' + rows[0].typeName.capitalize() + '>' +
                '*\n_*min*  `' + min.formatISK(2, 3, '.', ',') + ' ISK`_ \n_*max*  `' + max.formatISK(2, 3, '.', ',') + ' ISK`_ \n',
                thumb_url: self.imageServer + '/Type/' + typeId + '_64.png',
                mrkdwn_in: [
                  'text'
                ]
              }],
              mrkdwn_in: [
                'text'
              ]
            });
          });
      })
      .catch((err) => {
        bot.reply(message, {
          text: '',
          attachments: [{
            color: 'danger',
            title: 'fetch failed: ' + err
          }]
        });
      });
  }

  _fetchDb(sqliteDb, item) {
    return new Promise((resolve, reject) => {
      sqliteDb.all('SELECT typeID, typeName FROM invTypes WHERE typeName = "' + item + '" COLLATE NOCASE', (err, rows) => {
        if (err) {
          return reject(err);
        }

        if (rows.length === 0) {
          return reject('Item not found');
        }

        resolve(rows);
      });
    });
  }

  static tradeHubs() {
    return {
      jita: '30000142',
      hek: '30002053',
      amarr: '30002187',
      rens: '30002510',
      dodixie: '30002659'
    };
  }

  static globalHelp() {
    return '• [!central] -- command that can return the price of an item or a predefined group\n' +
      '• [!industry] -- command that ca return all actual industry jobs\n' +
      '• [!industry-history] [lastMonth|lastWeek|last2Weeks] -- command tha can return all complete industry jobs';
  }

  static help() {
    return {
      color: 'good',
      title: '!central',
      pretext: 'EvE Central Bot Help',
      text: 'this help'
    };
  }

  static hubHelp() {
    return {
      title: '!central hub [<hubname>]',
      text: 'should return the actual tradehub of which the bot should request prices' +
      '\n[<hubname>] can be amarr, jita, dodixie, rens or hek' +
      '\nif <hubname> is entered this tradehub should be set as station for the channel where its entered'
    };
  }

  static pricetypeHelp() {
    return {
      title: '!central price [<pricetype>]',
      text: 'should return the actual pricetype for which the bot should request prices from a hub' +
      '\n[<pricetype>] can either be sell or buy' +
      '\nif <pricetype> is entered this pricetag should be set as pricelist for the channel where its entered'
    };
  }

  static directhelp() {
    return {
      title: '!central "<typename>" [<pricetype>] [<hubname>]',
      text: 'should return the actual price from the eve-central - api.' +
      '\n*"<typename>"* type of good that should be check with the eve-central-api' +
      '\n*must be within quotation marks* so the typename can be validated to the typename/typeid lookuptable.' +
      '\n[<pricetype>] can either be sell or buy' +
      '\n[<hubname>] can be amarr, jita, dodixie, rens or hek' +
      '\nif <pricetype> is entered the api should return the price for the requested pricetag, ignoring the standard pricetag set for this channel.' +
      '\nif <hubname> is entered the api should return the price for the requested hub, ignoring the standard hub set for this channel.',
      mrkdwn_in: [
        'text'
      ]
    };
  }

  static groupHelp() {
    return {
      title: '!central group <groupname> - not implemented yet',
      text: 'should return a grouped pricelist for a special group ' +
      '<groupname> can be ore, cpore, minerals, p1, p2, p3, p4, ... (can be extendend)',
      mrkdwn_in: [
        'text'
      ]
    };
  }

  static allHelp() {
    return [
      EveCentral.help(),
      EveCentral.hubHelp(),
      EveCentral.pricetypeHelp(),
      EveCentral.directhelp(),
      EveCentral.groupHelp()
    ];
  }
}

module.exports = EveCentral;
<|endoftext|>


<commit_before>import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
// import { linkTo } from '@storybook/addon-links';
import { Components } from 'meteor/vulcan:core';
import 'meteor/vulcan:forms';

const options = [{
    value: 'Afghan',
    label: 'Afghan',
  },
  {
    value: 'Albanais',
    label: 'Albanais',
  },
  {
    value: 'Algérien',
    label: 'Algérien',
  },
  {
    value: 'Allemand',
    label: 'Allemand',
  },
  {
    value: 'Belge',
    label: 'Belge',
  },
  {
    value: 'Beninois',
    label: 'Beninois',
  },
  {
    value: 'Bosniaque',
    label: 'Bosniaque',
  },
  {
    value: 'Botswanais',
    label: 'Botswanais',
  }]

storiesOf('vulcan-forms')
  .add('FormError - message', () => (
    <Components.FormError
      error={{
        message: 'An error message',
      }}
    />
  ))
  .add('FormError intl - no properties', () => (
    <Components.FormError
      error={{
        id: 'intl-id',
      }}
    />
  ))
  .add('FormError intl', () => (
    <Components.FormError
      error={{
        id: 'intl-id',
        properties: {
          name: 'address.street',
        },
      }}
    />
  ))
  .add('FormNestedArrayLayout - nested item before/after components', () => (
    <Components.FormNestedArray
      value={[{ name: 'Jane' }, { name: 'DELETED' }, { name: 'John' }]}
      deletedValues={['peoples.1']}
      path="peoples"
      formComponents={{
        ...Components,
        FormNestedItem: () => (

            <input />

        ),
      }}
      errors={[]}
      updateCurrentValues={action('updateCurrentValues')}
      arrayField={{
        beforeComponent: props => (

            BEFORE {props.itemIndex} {props.visibleItemIndex}

        ),
        afterComponent: props =>
AFTER
,
      }}
    />
  ))
  .add('Form base-controls MuiSuggest', () => (
    <Components.MuiSuggest
      options={options}
    />
  ));
<commit_msg>add MuiRequiredIndicator base-controls story<commit_after>import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
// import { linkTo } from '@storybook/addon-links';
import { Components } from 'meteor/vulcan:core';
import 'meteor/vulcan:forms';
import { withKnobs, boolean} from '@storybook/addon-knobs';

const vulcan_forms = storiesOf('vulcan-forms', module);
vulcan_forms.addDecorator(withKnobs);

const options = [{
    value: 'Afghan',
    label: 'Afghan',
  },
  {
    value: 'Albanais',
    label: 'Albanais',
  },
  {
    value: 'Algérien',
    label: 'Algérien',
  },
  {
    value: 'Allemand',
    label: 'Allemand',
  },
  {
    value: 'Belge',
    label: 'Belge',
  },
  {
    value: 'Beninois',
    label: 'Beninois',
  },
  {
    value: 'Bosniaque',
    label: 'Bosniaque',
  },
  {
    value: 'Botswanais',
    label: 'Botswanais',
  }]

vulcan_forms
  .add('FormError - message', () => (
    <Components.FormError
      error={{
        message: 'An error message',
      }}
    />
  ))
  .add('FormError intl - no properties', () => (
    <Components.FormError
      error={{
        id: 'intl-id',
      }}
    />
  ))
  .add('FormError intl', () => (
    <Components.FormError
      error={{
        id: 'intl-id',
        properties: {
          name: 'address.street',
        },
      }}
    />
  ))
  .add('FormNestedArrayLayout - nested item before/after components', () => (
    <Components.FormNestedArray
      value={[{ name: 'Jane' }, { name: 'DELETED' }, { name: 'John' }]}
      deletedValues={['peoples.1']}
      path="peoples"
      formComponents={{
        ...Components,
        FormNestedItem: () => (

            <input />

        ),
      }}
      errors={[]}
      updateCurrentValues={action('updateCurrentValues')}
      arrayField={{
        beforeComponent: props => (

            BEFORE {props.itemIndex} {props.visibleItemIndex}

        ),
        afterComponent: props =>
AFTER
,
      }}
    />
  ))
  .add('Form base-controls MuiSuggest', () => (
    <Components.MuiSuggest
      options={options}
    />
  )).add('Form base-controls MuiRequiredIndicator', () =>{
    const optional = boolean('optional', false);
    return <Components.RequiredIndicator optional={optional} />;
  });
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */
"use strict";
const log4js = require('log4js');
const logger = log4js.getLogger('kronos-service-manager:channel');
//logger.setLevel(log4js.levels.ERROR);

const fifo = require('fifo');

const kronosStep = require('kronos-step');
const endpointImplementation = kronosStep.endpointImplementation;

const expression = require('expression');

/**
 *  Template for the Channel object
 */
const RootChannel = {
	toString() {
		return this.name;
	}
};

/*
 * create two endpoints glued together to form a channel
 * @return array with the two channles
 */
exports.create = function (step1, ep1, step2, ep2) {
	const name = `${step1.name}/${ep1.name}->${step2.name}/${ep2.name}`;
	logger.debug(`Create Channel: ${name}`);

	let imp1; // endpoint implementation 1
	let imp2; // endpoint implementation 2

	// In this case we just swap the endpoints, so that the next
	// IF matches and creates the connection
	if (ep1.isInAndCanBePassive && ep2.isOutAndCanBeActive) {
		logger.debug(`In Passive & Out Active`);

		logger.debug(`Channel swap ${ep1} <> ${ep2}`);
		const t = ep1;
		ep1 = ep2;
		ep2 = t;
	}

	const exp = expression.createExpressionContext();

	exp.properties = {
		fileName: "aName"
	};

	if (ep1.isOutAndCanBeActive && ep2.isInAndCanBePassive) {
		logger.debug(`Out Active & In Passive`);

		// this is the handle where data passes between endpointA and endpointB
		let generatorObject;

		imp1 = function (manager) {
			logger.debug(`Imp1: Endpoint Set '${name}'`);

			const myGen = function* () {
				logger.debug(`Imp1: Initialize iterator '${name}'`);
				let iterator = generatorObject();
				iterator.next();
				logger.debug(`Imp1: Iterator initialized '${name}'`);

				while (true) {
					logger.debug(`Imp1: Generator called '${name}'`);

					let request = yield;

					logger.debug(`Imp1: Got request ${JSON.stringify(request)}`);

					logger.debug(`Imp1: Transport the request to next. '${name}'`);
					iterator.next(request);

					/*
					for(let request of generatorObject()) {
					  exp.properties = request.info;
					  request.info = exp.expand({
					    someKey: "aValue",
					    name: "${fileName}"
					  });
					  yield request;
					}
					*/
				}
			};

			logger.debug(`Imp1: Generator created '${name}'`);
			let iterator = myGen();
			//iterator.next();
			return iterator;
		};

		imp2 = function (manager, aGeneratorFunction) {
			if (aGeneratorFunction === undefined) {
				throw ("Generator function expected but not there");
			}
			logger.debug(`Imp2: Endpoint Set '${name}'`);
			generatorObject = aGeneratorFunction;
			return undefined;
		};

	} else if (ep1.isOutAndCanBePassive && ep2.isInAndCanBeActive) {
		logger.debug(`Out Passive & In Active`);
		throw (`Out Passive & In Active`);
	} else if (ep1.isInAndCanBeActive && ep2.isOutAndCanBePassive) {
		logger.debug(`In Active & Out Passive`);
		throw (`In Active & Out Passive`);
	} else if (ep1.isInAndCanBeActive && ep2.isOutAndCanBeActive) {
		throw (`In Active & Out Active`);
	} else if (ep1.isInAndCanBePassive && ep2.isOutAndCanBePassive) {
		logger.debug(`In Passive & Out Passive`);
		logger.debug(`Else`);

		let requests = fifo();

		imp1 = function (manager) {
			const myGen = function* () {
				while (true) {
					let request = yield;

					logger.debug(`imp1: got request: ${name}: ${JSON.stringify(request.info)}`);
					requests.push(request);
				}
			};

			const go = myGen();
			go.next();
			return go;
		};

		imp2 = function (manager, genertor) {
			logger.debug(`imp2: generator: ${genertor}`);

			logger.debug(`imp2: start`);

			const myGen = function* () {
				while (true) {
					let request = requests.shift();

					logger.debug(`imp2: got request: ${JSON.stringify(request.info)}`);

					if (!request) {
						logger.debug(`provide request ${name}: *** nothing more to provide ***`);
						break;
					}

					//exp.properties = { fileName: "aName" };
					exp.properties = request.info;
					// ep2.contentInfo
					request.info = exp.expand({
						someKey: "aValue",
						name: "${fileName}"
					});

					logger.debug(`provide request ${name}: ${JSON.stringify(request.info)}`);

					yield request;
				}
			};
			return myGen();
		};
	} else {
		throw (`No channel for directions '${ep1.direction}' '${ep2.direction}'`);
	}

	return Object.create(RootChannel, {
		name: {
			value: name
		},
		endpointA: {
			value: kronosStep.createEndpoint(ep1.name, {
				description: `Channel ${name}`,
				implementation: imp1
			}, ep1)
		},
		endpointB: {
			value: kronosStep.createEndpoint(ep2.name, {
				description: `Channel ${name}`,
				implementation: imp2
			}, ep2)
		}
	});
};
<commit_msg>remove log<commit_after>/* jslint node: true, esnext: true */
"use strict";
const log4js = require('log4js');
const logger = log4js.getLogger('kronos-service-manager:channel');
//logger.setLevel(log4js.levels.ERROR);

const fifo = require('fifo');

const kronosStep = require('kronos-step');
const endpointImplementation = kronosStep.endpointImplementation;

const expression = require('expression');

/**
 *  Template for the Channel object
 */
const RootChannel = {
	toString() {
		return this.name;
	}
};

/*
 * create two endpoints glued together to form a channel
 * @return array with the two channles
 */
exports.create = function (step1, ep1, step2, ep2) {
	const name = `${step1.name}/${ep1.name}->${step2.name}/${ep2.name}`;
	logger.debug(`Create Channel: ${name}`);

	let imp1; // endpoint implementation 1
	let imp2; // endpoint implementation 2

	// In this case we just swap the endpoints, so that the next
	// IF matches and creates the connection
	if (ep1.isInAndCanBePassive && ep2.isOutAndCanBeActive) {
		logger.debug(`In Passive & Out Active`);

		logger.debug(`Channel swap ${ep1} <> ${ep2}`);
		const t = ep1;
		ep1 = ep2;
		ep2 = t;
	}

	const exp = expression.createExpressionContext();

	exp.properties = {
		fileName: "aName"
	};

	if (ep1.isOutAndCanBeActive && ep2.isInAndCanBePassive) {
		logger.debug(`Out Active & In Passive`);

		// this is the handle where data passes between endpointA and endpointB
		let generatorObject;

		imp1 = function (manager) {
			logger.debug(`Imp1: Endpoint Set '${name}'`);

			const myGen = function* () {
				logger.debug(`Imp1: Initialize iterator '${name}'`);
				let iterator = generatorObject();
				iterator.next();
				logger.debug(`Imp1: Iterator initialized '${name}'`);

				while (true) {
					logger.debug(`Imp1: Generator called '${name}'`);

					let request = yield;

					//logger.debug(`Imp1: Got request ${JSON.stringify(request)}`);

					logger.debug(`Imp1: Transport the request to next. '${name}'`);
					iterator.next(request);

					/*
					for(let request of generatorObject()) {
					  exp.properties = request.info;
					  request.info = exp.expand({
					    someKey: "aValue",
					    name: "${fileName}"
					  });
					  yield request;
					}
					*/
				}
			};

			logger.debug(`Imp1: Generator created '${name}'`);
			let iterator = myGen();
			//iterator.next();
			return iterator;
		};

		imp2 = function (manager, aGeneratorFunction) {
			if (aGeneratorFunction === undefined) {
				throw ("Generator function expected but not there");
			}
			logger.debug(`Imp2: Endpoint Set '${name}'`);
			generatorObject = aGeneratorFunction;
			return undefined;
		};

	} else if (ep1.isOutAndCanBePassive && ep2.isInAndCanBeActive) {
		logger.debug(`Out Passive & In Active`);
		throw (`Out Passive & In Active`);
	} else if (ep1.isInAndCanBeActive && ep2.isOutAndCanBePassive) {
		logger.debug(`In Active & Out Passive`);
		throw (`In Active & Out Passive`);
	} else if (ep1.isInAndCanBeActive && ep2.isOutAndCanBeActive) {
		throw (`In Active & Out Active`);
	} else if (ep1.isInAndCanBePassive && ep2.isOutAndCanBePassive) {
		logger.debug(`In Passive & Out Passive`);
		logger.debug(`Else`);

		let requests = fifo();

		imp1 = function (manager) {
			const myGen = function* () {
				while (true) {
					let request = yield;

					logger.debug(`imp1: got request: ${name}: ${JSON.stringify(request.info)}`);
					requests.push(request);
				}
			};

			const go = myGen();
			go.next();
			return go;
		};

		imp2 = function (manager, genertor) {
			logger.debug(`imp2: generator: ${genertor}`);

			logger.debug(`imp2: start`);

			const myGen = function* () {
				while (true) {
					let request = requests.shift();

					logger.debug(`imp2: got request: ${JSON.stringify(request.info)}`);

					if (!request) {
						logger.debug(`provide request ${name}: *** nothing more to provide ***`);
						break;
					}

					//exp.properties = { fileName: "aName" };
					exp.properties = request.info;
					// ep2.contentInfo
					request.info = exp.expand({
						someKey: "aValue",
						name: "${fileName}"
					});

					logger.debug(`provide request ${name}: ${JSON.stringify(request.info)}`);

					yield request;
				}
			};
			return myGen();
		};
	} else {
		throw (`No channel for directions '${ep1.direction}' '${ep2.direction}'`);
	}

	return Object.create(RootChannel, {
		name: {
			value: name
		},
		endpointA: {
			value: kronosStep.createEndpoint(ep1.name, {
				description: `Channel ${name}`,
				implementation: imp1
			}, ep1)
		},
		endpointB: {
			value: kronosStep.createEndpoint(ep2.name, {
				description: `Channel ${name}`,
				implementation: imp2
			}, ep2)
		}
	});
};
<|endoftext|>


<commit_before>joint.shapes.logic.Box21 = joint.shapes.logic.IO.extend({
    markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g>'+
           '<circle class="input input1"/>'+
           '<circle  class="input input2"/><circle class="output"/>'+
           '<path class="wire wireIn1"/>'+
           '<path class="wire wireIn2"/>'+
           '<path class="wire wireOut"/><text/></g>',
    defaults: joint.util.deepSupplement({
        type: 'logic.Timer',
        size: { width: 60, height: 40 },
        attrs: {
            '.wire': { stroke: 'black'},
            '.wireIn1': { ref: '.body', 'ref-x': -20, 'ref-y': 0.3,
                          d: 'M 0 0 L 20 0' },
            '.wireIn2': { ref: '.body', 'ref-x': -20, 'ref-y': 0.7,
                          d: 'M 0 0 L 20 0' },
            '.wireOut': { ref: '.body', 'ref-dx': 0, 'ref-y': 0.5,
                          d: 'M 0 0 L 20 0' },
            '.input1': { ref: '.body', 'ref-x': -27, 'ref-y': 0.3,
                         magnet: 'passive', port: 'in1' },
            '.input2': { ref: '.body', 'ref-x': -27, 'ref-y': 0.7,
                         magnet: 'passive', port: 'in2' },
            '.output': { ref: '.body', 'ref-dx': 27, 'ref-y': 0.5,
                         magnet: true, port: 'out1' },
            text: {'text-transform': 'none', 'ref-x': 0, 'text-anchor': 'left'}
        }
    }, joint.shapes.logic.IO.prototype.defaults)
});

function createBox21Class(type, text) {
    joint.shapes.logic[type] = joint.shapes.logic.Box21.extend({
        defaults: joint.util.deepSupplement({
            type: 'logic.' + type,
            attrs: {
                text: {text: text}
            }
        }, joint.shapes.logic.Box21.prototype.defaults)
    })
}
joint.shapes.logic.Box11 = joint.shapes.logic.IO.extend({
    markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g>'+
           '<circle class="input"/>'+
           '<circle class="output"/>'+
           '<path class="wire wireIn"/>'+
           '<path class="wire wireOut"/><text/></g>',
    defaults: joint.util.deepSupplement({
        type: 'logic.Timer',
        size: { width: 60, height: 30 },
        attrs: {
            '.wire': { stroke: 'black'},
            '.wireIn': { ref: '.body', 'ref-x': -20, 'ref-y': 0.5,
                          d: 'M 0 0 L 20 0' },
            '.wireOut': { ref: '.body', 'ref-dx': 0, 'ref-y': 0.5,
                          d: 'M 0 0 L 20 0' },
            '.input': { ref: '.body', 'ref-x': -27, 'ref-y': 0.5,
                        magnet: 'passive', port: 'in1' },
            '.output': { ref: '.body', 'ref-dx': 27, 'ref-y': 0.5,
                         magnet: true, port: 'out1' },
            text: {'text-transform': 'none'}
        }
    }, joint.shapes.logic.IO.prototype.defaults)
});

function createBox11Class(type, text) {
    joint.shapes.logic[type] = joint.shapes.logic.Box11.extend({
        defaults: joint.util.deepSupplement({
            type: 'logic.' + type,
            attrs: {
                text: {text: text}
            }
        }, joint.shapes.logic.Box11.prototype.defaults)
    })
}

createBox21Class('Timer', 'start\nreset');
createBox21Class('DFlipFlop', 'D\nclock');
createBox11Class('UpEdge', 'up edge');
createBox11Class('DownEdge', 'down edge');
createBox11Class('BothEdge', 'both edge');

var TYPE2SHAPE = {
    'switch': joint.shapes.logic.Input,
    relay: joint.shapes.logic.Output,
    or: joint.shapes.logic.Or,
    and: joint.shapes.logic.And,
    xor: joint.shapes.logic.Xor,
    not: joint.shapes.logic.Not,
    timer: joint.shapes.logic.Timer,
    d_flipflop: joint.shapes.logic.DFlipFlop,
    up_edge: joint.shapes.logic.UpEdge,
    down_edge: joint.shapes.logic.DownEdge,
    both_edge: joint.shapes.logic.BothEdge
};

function graphId(id) {
    return "schema-element-" + id;
}

function parseId(id) {
    return parseInt(id.match(/schema-element-(\d+)/)[1]);
}

function parsePort(port) {
    return parseInt(port.match(/(?:in|out)(\d+)/)[1]);
}

function createCell(model) {
    return new TYPE2SHAPE[model.get('type')]({
        id: graphId(model.get('id')),
        position: {x: model.get('x'), y: model.get('y')},
        element: model
    });
}

function createWire(model) {
    return model.graphLink = new joint.shapes.logic.Wire({
        source: { id: graphId(model.get('source_id')),
                  port: 'out' + model.get('source_output') },
        target: { id: graphId(model.get('target_id')),
                  port: 'in' + model.get('target_input') },
        vertices: model.get('vertices') || [],
        con: model
    });
}

function loadGraph(graph, schema) {
    if(!schema) {
        return;
    }

    graph.elementStore = createSubStore('Element', 'elements', schema.get('id'));
    graph.connectionStore =
        createSubStore('Connection', 'connections', schema.get('id'));

    graph.elementStore.load({
        callback: function(records, operation, success) {
            var nodes = records.map(createCell);

            graph.connectionStore.load({
                callback: function(records, operation, success) {
                    var wires = records.map(createWire);
                    graph.resetCells(nodes.concat(wires));
                    replayStatusCache(graph);
                }
            });
        }
    });
}

function updateElementPosition(cell) {
    var element = cell.attributes.element;
    if(!element) return;
    var pos = cell.position();
    element.set('x', pos.x);
    element.set('y', pos.y);
}

function updateConnection(graph, link) {
    var target = link.attributes.target;
    if(!target.id) return; //not yet connected

    var source = link.attributes.source;
    var vertices = link.attributes.vertices || [];
    if(!link.attributes.con) {
        //new link
        link.attributes.con = graph.connectionStore.add({
            source_id: parseId(source.id),
            source_output: parsePort(source.port),
            target_id: parseId(target.id),
            target_input: parsePort(target.port),
            vertices: vertices
        })[0];
    } else {
        //updated link
        var model = link.attributes.con;
        model.set("source_id", parseId(source.id));
        model.set("source_output", parsePort(source.port));
        model.set("target_id", parseId(target.id));
        model.set("target_input", parsePort(target.port));
        model.set("vertices", vertices);
    }
    link.attributes.con.graphLink = link;
}

function removeConnection(graph, link) {
    if(!graph.connectionStore) return;   //don't remove when switching schema
    var target = link.attributes.target;
    if(!target || !target.id || !link.attributes.con) return; //not yet connected
    link.attributes.con.drop();
}

function commitSchemaChanges(graph) {
    if(graph.elementStore) {
        graph.elementStore.sync();
    }
    if(graph.connectionStore) {
        graph.connectionStore.sync();
    }
}

function createSchemaToolbarHandler(graph, type) {
    return function() {
        var model = graph.elementStore.add({
            type: type,
            x: 50, y: 50
        })[0];
        graph.elementStore.sync({
            callback: function() {
                graph.addCell(createCell(model));
            }
        });
    };
}

function createSchemaToolbar(graph) {
    var gates = [];
    for(var type in TYPE2SHAPE) {
        gates.push({
            text: type,
            handler: createSchemaToolbarHandler(graph, type)
        });
    }
    return Ext.create('Ext.toolbar.Toolbar', {
        cls: 'toolbar',
        items: gates
    });
}

function updateStatusCache(graph, message) {
    graph.statusCache[message.id] = message;
}

function replayStatusCache(graph) {
    if(!graph.paper) return;
    for(var id in graph.statusCache) {
        var message = graph.statusCache[id];
        handleNotif(graph, graph.paper, message);
    }
}

function handleNotif(graph, paper, message) {
    updateStatusCache(graph, message);

    var cell;
    if(message.type == 'relay' || message.type == 'switch') {
        cell = graph.getCell(graphId(message.id));
    } else if(message.type == 'connection' && graph.connectionStore) {
        var element = graph.connectionStore.getById(message.id);
        if(element) {
            cell = graph.connectionStore.getById(message.id).graphLink;
        }
    }

    if(cell) {
        var view = V(paper.findViewByModel(cell).el);
        view.toggleClass('on', message.value);
        view.toggleClass('off', !message.value);
    }
}

function displayTimerForm(graph, element) {
    var form = new Ext.form.Panel({
        title: 'Timer params',
        defaults: {
            padding: 10,
        },
        floating: true,
        draggable: true,
        closable: true,
        defaultType: 'numberfield',
        items: [{
            fieldLabel: 'Delay [ms]',
            name: 'delay',
            minValue: 0
        }],
        buttons: [{
            text: 'Save',
            handler: function() {
                var form = this.up('form');
                var values = form.getForm().getFieldValues();
                element.set('config', values);
                graph.elementStore.sync();
                form.close();
            }
        }]
    });
    form.getForm().setValues(element.get("config") || {});
    form.show();
}

function handleClick(graph, cell) {
    var element = cell.model.attributes.element;
    if(!element) return;
    var type = element.get("type");
    if(type == 'switch') {
        Ext.Ajax.request({
            url: '/controls/toggle/' + element.get('id'),
            method : "PUT",
            headers: {'Content-Type': 'application/json'},
            jsonData: true
        });
    } if(type == 'timer') {
        displayTimerForm(graph, element);
    }
}

function createSchema(name, grid) {
    var graph = new joint.dia.Graph;
    graph.statusCache = {};

    var toolbar = createSchemaToolbar(graph);
    toolbar.disable();

    function newGraph(selections) {
            delete graph.elementStore;
            delete graph.connectionStore;
            graph.clear();

            if(selections.length > 0 && !selections[0].phantom) {
                toolbar.enable();
                loadGraph(graph, selections[0]);
            } else {
                toolbar.disable();
            }
    }

    grid.getSelectionModel().on('selectionchange',
        function(selModel, selections)  {
            newGraph(selections);
        }
    );
    grid.getStore().on('write',
        function(store, operation) {
            newGraph(operation.getRecords());
        }
    );

    var batches = 0;
    graph.on('change:position', updateElementPosition);
    graph.on('change:source', function(link) {updateConnection(graph, link);});
    graph.on('change:target', function(link) {updateConnection(graph, link);});
    graph.on('change:vertices', function(link) {updateConnection(graph, link);});
    graph.on('remove', function(link) {removeConnection(graph, link);});
    graph.on('batch:start', function() {batches++;});
    graph.on('batch:stop', function() {
        if(--batches == 0) commitSchemaChanges(graph);
    });

    return {
        region: 'center',
        html : '<div id="paper" class="paper" style="width: 100%; height: 100%"/>',
        dockedItems: [toolbar],
        listeners: {
            afterlayout: function() {
                var paper = new joint.dia.Paper({
                    el: $('#paper'),
                    width: '100%',
                    height: '100%',
                    model: graph,
                    gridSize: 5,
                    snapLinks: true,
                    defaultLink: new joint.shapes.logic.Wire,
                    validateConnection: function(vs, ms, vt, mt, e, vl) {
                        if (e === 'target') {
                            // target requires an input port to connect
                            if (!mt || !mt.getAttribute('class') || mt.getAttribute('class').indexOf('input') < 0) return false;

                            // check whether the port is being already used
                            var portUsed = _.find(this.model.getLinks(), function(link) {
                                return (link.id !== vl.model.id &&
                                        link.get('target').id === vt.model.id &&
                                        link.get('target').port === mt.getAttribute('port'));
                            });

                            return !portUsed;
                        } else { // e === 'source'

                            // source requires an output port to connect
                            return ms && ms.getAttribute('class') && ms.getAttribute('class').indexOf('output') >= 0;
                        }
                    }
                });
                graph.paper = paper;
                var notifListener = new MyWebSocket({
                    url: 'ws://' + window.location.host + '/notifs'
                });
                notifListener.on('message', function(message) {
                    handleNotif(graph, paper, Ext.decode(message));
                });
                paper.on('cell:pointerclick', function(cell) {
                    handleClick(graph, cell);
                });
            }
        }
    };
}
<commit_msg>web: Can delete an element by right-clicking it<commit_after>joint.shapes.logic.Box21 = joint.shapes.logic.IO.extend({
    markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g>'+
           '<circle class="input input1"/>'+
           '<circle  class="input input2"/><circle class="output"/>'+
           '<path class="wire wireIn1"/>'+
           '<path class="wire wireIn2"/>'+
           '<path class="wire wireOut"/><text/></g>',
    defaults: joint.util.deepSupplement({
        type: 'logic.Timer',
        size: { width: 60, height: 40 },
        attrs: {
            '.wire': { stroke: 'black'},
            '.wireIn1': { ref: '.body', 'ref-x': -20, 'ref-y': 0.3,
                          d: 'M 0 0 L 20 0' },
            '.wireIn2': { ref: '.body', 'ref-x': -20, 'ref-y': 0.7,
                          d: 'M 0 0 L 20 0' },
            '.wireOut': { ref: '.body', 'ref-dx': 0, 'ref-y': 0.5,
                          d: 'M 0 0 L 20 0' },
            '.input1': { ref: '.body', 'ref-x': -27, 'ref-y': 0.3,
                         magnet: 'passive', port: 'in1' },
            '.input2': { ref: '.body', 'ref-x': -27, 'ref-y': 0.7,
                         magnet: 'passive', port: 'in2' },
            '.output': { ref: '.body', 'ref-dx': 27, 'ref-y': 0.5,
                         magnet: true, port: 'out1' },
            text: {'text-transform': 'none', 'ref-x': 0, 'text-anchor': 'left'}
        }
    }, joint.shapes.logic.IO.prototype.defaults)
});

function createBox21Class(type, text) {
    joint.shapes.logic[type] = joint.shapes.logic.Box21.extend({
        defaults: joint.util.deepSupplement({
            type: 'logic.' + type,
            attrs: {
                text: {text: text}
            }
        }, joint.shapes.logic.Box21.prototype.defaults)
    })
}
joint.shapes.logic.Box11 = joint.shapes.logic.IO.extend({
    markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g>'+
           '<circle class="input"/>'+
           '<circle class="output"/>'+
           '<path class="wire wireIn"/>'+
           '<path class="wire wireOut"/><text/></g>',
    defaults: joint.util.deepSupplement({
        type: 'logic.Timer',
        size: { width: 60, height: 30 },
        attrs: {
            '.wire': { stroke: 'black'},
            '.wireIn': { ref: '.body', 'ref-x': -20, 'ref-y': 0.5,
                          d: 'M 0 0 L 20 0' },
            '.wireOut': { ref: '.body', 'ref-dx': 0, 'ref-y': 0.5,
                          d: 'M 0 0 L 20 0' },
            '.input': { ref: '.body', 'ref-x': -27, 'ref-y': 0.5,
                        magnet: 'passive', port: 'in1' },
            '.output': { ref: '.body', 'ref-dx': 27, 'ref-y': 0.5,
                         magnet: true, port: 'out1' },
            text: {'text-transform': 'none'}
        }
    }, joint.shapes.logic.IO.prototype.defaults)
});

function createBox11Class(type, text) {
    joint.shapes.logic[type] = joint.shapes.logic.Box11.extend({
        defaults: joint.util.deepSupplement({
            type: 'logic.' + type,
            attrs: {
                text: {text: text}
            }
        }, joint.shapes.logic.Box11.prototype.defaults)
    })
}

createBox21Class('Timer', 'start\nreset');
createBox21Class('DFlipFlop', 'D\nclock');
createBox11Class('UpEdge', 'up edge');
createBox11Class('DownEdge', 'down edge');
createBox11Class('BothEdge', 'both edge');

var TYPE2SHAPE = {
    'switch': joint.shapes.logic.Input,
    relay: joint.shapes.logic.Output,
    or: joint.shapes.logic.Or,
    and: joint.shapes.logic.And,
    xor: joint.shapes.logic.Xor,
    not: joint.shapes.logic.Not,
    timer: joint.shapes.logic.Timer,
    d_flipflop: joint.shapes.logic.DFlipFlop,
    up_edge: joint.shapes.logic.UpEdge,
    down_edge: joint.shapes.logic.DownEdge,
    both_edge: joint.shapes.logic.BothEdge
};

function graphId(id) {
    return "schema-element-" + id;
}

function parseId(id) {
    return parseInt(id.match(/schema-element-(\d+)/)[1]);
}

function parsePort(port) {
    return parseInt(port.match(/(?:in|out)(\d+)/)[1]);
}

function createCell(model) {
    return new TYPE2SHAPE[model.get('type')]({
        id: graphId(model.get('id')),
        position: {x: model.get('x'), y: model.get('y')},
        element: model
    });
}

function createWire(model) {
    return model.graphLink = new joint.shapes.logic.Wire({
        source: { id: graphId(model.get('source_id')),
                  port: 'out' + model.get('source_output') },
        target: { id: graphId(model.get('target_id')),
                  port: 'in' + model.get('target_input') },
        vertices: model.get('vertices') || [],
        con: model
    });
}

function loadGraph(graph, schema) {
    if(!schema) {
        return;
    }

    graph.elementStore = createSubStore('Element', 'elements', schema.get('id'));
    graph.connectionStore =
        createSubStore('Connection', 'connections', schema.get('id'));

    graph.elementStore.load({
        callback: function(records, operation, success) {
            var nodes = records.map(createCell);

            graph.connectionStore.load({
                callback: function(records, operation, success) {
                    var wires = records.map(createWire);
                    graph.resetCells(nodes.concat(wires));
                    replayStatusCache(graph);
                }
            });
        }
    });
}

function updateElementPosition(cell) {
    var element = cell.attributes.element;
    if(!element) return;
    var pos = cell.position();
    element.set('x', pos.x);
    element.set('y', pos.y);
}

function updateConnection(graph, link) {
    var target = link.attributes.target;
    if(!target.id) return; //not yet connected

    var source = link.attributes.source;
    var vertices = link.attributes.vertices || [];
    if(!link.attributes.con) {
        //new link
        link.attributes.con = graph.connectionStore.add({
            source_id: parseId(source.id),
            source_output: parsePort(source.port),
            target_id: parseId(target.id),
            target_input: parsePort(target.port),
            vertices: vertices
        })[0];
    } else {
        //updated link
        var model = link.attributes.con;
        model.set("source_id", parseId(source.id));
        model.set("source_output", parsePort(source.port));
        model.set("target_id", parseId(target.id));
        model.set("target_input", parsePort(target.port));
        model.set("vertices", vertices);
    }
    link.attributes.con.graphLink = link;
}

function removeItem(graph, item) {
    if(!graph.connectionStore) return;   //don't remove when switching schema
    if(item.isLink()) {
        var target = item.attributes.target;
        if(!target || !target.id || !item.attributes.con) return; //not yet connected
        item.attributes.con.drop();
    } else {
        if(!item.attributes.element) return;
        item.attributes.element.drop();
    }
    //graph.commitChanges called by batch:stop
}

function createSchemaToolbarHandler(graph, type) {
    return function() {
        var model = graph.elementStore.add({
            type: type,
            x: 50, y: 50
        })[0];
        graph.elementStore.sync({
            callback: function() {
                graph.addCell(createCell(model));
            }
        });
    };
}

function createSchemaToolbar(graph) {
    var gates = [];
    for(var type in TYPE2SHAPE) {
        gates.push({
            text: type,
            handler: createSchemaToolbarHandler(graph, type)
        });
    }
    return Ext.create('Ext.toolbar.Toolbar', {
        cls: 'toolbar',
        items: gates
    });
}

function updateStatusCache(graph, message) {
    graph.statusCache[message.id] = message;
}

function replayStatusCache(graph) {
    if(!graph.paper) return;
    for(var id in graph.statusCache) {
        var message = graph.statusCache[id];
        handleNotif(graph, graph.paper, message);
    }
}

function handleNotif(graph, paper, message) {
    updateStatusCache(graph, message);

    var cell;
    if(message.type == 'relay' || message.type == 'switch') {
        cell = graph.getCell(graphId(message.id));
    } else if(message.type == 'connection' && graph.connectionStore) {
        var element = graph.connectionStore.getById(message.id);
        if(element) {
            cell = graph.connectionStore.getById(message.id).graphLink;
        }
    }

    if(cell) {
        var view = V(paper.findViewByModel(cell).el);
        view.toggleClass('on', message.value);
        view.toggleClass('off', !message.value);
    }
}

function displayTimerForm(graph, element) {
    var form = new Ext.form.Panel({
        title: 'Timer params',
        defaults: {
            padding: 10,
        },
        floating: true,
        draggable: true,
        closable: true,
        defaultType: 'numberfield',
        items: [{
            fieldLabel: 'Delay [ms]',
            name: 'delay',
            minValue: 0
        }],
        buttons: [{
            text: 'Save',
            handler: function() {
                var form = this.up('form');
                var values = form.getForm().getFieldValues();
                element.set('config', values);
                graph.elementStore.sync();
                graph.commitChanges()
                form.close();
            }
        }]
    });
    form.getForm().setValues(element.get("config") || {});
    form.show();
}

function handleClick(graph, cell) {
    var element = cell.model.attributes.element;
    if(!element) return;
    var type = element.get("type");
    if(type == 'switch') {
        Ext.Ajax.request({
            url: '/controls/toggle/' + element.get('id'),
            method : "PUT",
            headers: {'Content-Type': 'application/json'},
            jsonData: true
        });
    } if(type == 'timer') {
        displayTimerForm(graph, element);
    }
}

function createSchema(name, grid) {
    var graph = new joint.dia.Graph;
    graph.statusCache = {};

    var delay = new Ext.util.DelayedTask(function(){
        if(graph.elementStore) graph.elementStore.sync();
        if(graph.connectionStore) graph.connectionStore.sync();
    });
    graph.commitChanges = function() {
        delay.delay(200);
    }

    var toolbar = createSchemaToolbar(graph);
    toolbar.disable();

    function newGraph(selections) {
            delete graph.elementStore;
            delete graph.connectionStore;
            graph.clear();

            if(selections.length > 0 && !selections[0].phantom) {
                toolbar.enable();
                loadGraph(graph, selections[0]);
            } else {
                toolbar.disable();
            }
    }

    grid.getSelectionModel().on('selectionchange',
        function(selModel, selections)  {
            newGraph(selections);
        }
    );
    grid.getStore().on('write',
        function(store, operation) {
            newGraph(operation.getRecords());
        }
    );

    graph.on('change:position', updateElementPosition);
    graph.on('change:source', function(link) {updateConnection(graph, link);});
    graph.on('change:target', function(link) {updateConnection(graph, link);});
    graph.on('change:vertices', function(link) {updateConnection(graph, link);});
    graph.on('remove', function(link) {removeItem(graph, link);});
    graph.on('batch:stop', function() {
        graph.commitChanges();
    });

    return {
        region: 'center',
        html : '<div id="paper" class="paper" style="width: 100%; height: 100%" oncontextmenu="return false;"/>',
        dockedItems: [toolbar],
        listeners: {
            afterlayout: function() {
                var paper = new joint.dia.Paper({
                    el: $('#paper'),
                    width: '100%',
                    height: '100%',
                    model: graph,
                    gridSize: 5,
                    snapLinks: true,
                    defaultLink: new joint.shapes.logic.Wire,
                    validateConnection: function(vs, ms, vt, mt, e, vl) {
                        if (e === 'target') {
                            // target requires an input port to connect
                            if (!mt || !mt.getAttribute('class') || mt.getAttribute('class').indexOf('input') < 0) return false;

                            // check whether the port is being already used
                            var portUsed = _.find(this.model.getLinks(), function(link) {
                                return (link.id !== vl.model.id &&
                                        link.get('target').id === vt.model.id &&
                                        link.get('target').port === mt.getAttribute('port'));
                            });

                            return !portUsed;
                        } else { // e === 'source'

                            // source requires an output port to connect
                            return ms && ms.getAttribute('class') && ms.getAttribute('class').indexOf('output') >= 0;
                        }
                    }
                });
                graph.paper = paper;
                var notifListener = new MyWebSocket({
                    url: 'ws://' + window.location.host + '/notifs'
                });
                notifListener.on('message', function(message) {
                    handleNotif(graph, paper, Ext.decode(message));
                });
                paper.on('cell:pointerclick', function(cell) {
                    handleClick(graph, cell);
                });
                paper.on('cell:pointerup', function(cell, event) {
                    if(event.button >= 2 && !cell.model.isLink()) {
                        cell.model.remove();  //will notify removeItem
                    }
                });
            }
        }
    };
}
<|endoftext|>


<commit_before>/**
 * classes.js
 * Andrea Tino - 2015
 */

/**
 * Detects and collects classes in the ScriptSharp source code.
 */
module.exports = function() {
  var nodeTypes = require('./JSParser/nodeTypes.js');
  var source = require('./source.js');

  var ast = null;
  var src = null;
  var classes = [];

  return {
    /**
     * Initializes the module.
     * _ast: A valid AST browser.
     * _src: The source string.
     */
    initialize: function(_ast, _src) {
      if (!ast) {
        throw 'Error: AST cannot be null or undefined!';
      }
      if (!src) {
        throw 'Error: Source cannot be null or undefined!';
      }

      ast = _ast;
      src = source(_src); // We directly create the locator
    },

    /**
     * Locates classes in the AST and stores all ids.
     */
    retrieveTypes: function() {
      if (!ast || !src) {
        throw 'Error: Initialization needed!';
      }

      ast.browseThroughNodes(nodeTypes.NODE_STATEMENT_EXPR, function(d) {
        // We can add it
        classes.push(null);
      });
    }
  };
};

<commit_msg>Update classes.js<commit_after>/**
 * classes.js
 * Andrea Tino - 2015
 */

/**
 * Detects and collects classes in the ScriptSharp source code.
 */
module.exports = function() {
  var nodeTypes = require('./JSParser/nodeTypes.js');

  var ast = null;
  var src = null;
  var classes = [];

  return {
    /**
     * Initializes the module.
     * _ast: A valid AST browser.
     * _src: A valid source locator.
     */
    initialize: function(_ast, _src) {
      if (!ast) {
        throw 'Error: AST cannot be null or undefined!';
      }
      if (!src) {
        throw 'Error: Source cannot be null or undefined!';
      }

      ast = _ast;
      src = _src;
    },

    /**
     * Locates classes in the AST and stores all ids.
     */
    retrieveTypes: function() {
      if (!ast || !src) {
        throw 'Error: Initialization needed!';
      }

      ast.browseThroughNodes(nodeTypes.NODE_STATEMENT_EXPR, function(d) {
        // We can add it
        classes.push(null);
      });
    }
  };
};

<|endoftext|>


<commit_before>'use strict';

var cleaner = exports;

var util      = require('util');
var async     = require('async');
var fs        = require('fs');
var node_path = require('path');
var expand    = require('fs-expand');

cleaner.clean = function (cwd, pkg, callback) {
  // Silently convert pkg.name to lowercase
  pkg.name = pkg.name.toLowerCase();

  async.each([
    'check_dirs',
    'clean_pkg_css',
    'clean_pkg_main',
    'clean_pkg_entries',
    'clean_pkg_as'
  ], function (task, done) {
    cleaner[task](cwd, pkg, done);

  }, function (err) {
    if (err) {
      return callback(err);
    }
    cleaner.check_entry(pkg, callback);
  });
};

// Cortex must be something to export
cleaner.check_entry = function (pkg, callback) {
  if (!pkg.main && !pkg.css.length && !pkg.entries.length) {
    return callback({
      code: 'CORTEX_NO_ENTRY',
      message: [
        'A package must do something. At least {{bold ONE}} of the 3 fields should be defined and existing:',
        '   - `{{bold cortex.main}}`   : it could be `require()`d',
        '   - `{{bold cortex.css}}`    : the css file(s) could be depent by other packages',
        '   - `{{bold cortex.entries}}`: could be accessed with `facade()`.'
      ].join('\n')
    });
  }

  var name_js = '.' + node_path.sep + pkg.name + '.js';
  if (~pkg.entries.indexOf(name_js)) {
    return callback({
      code: 'CORTEX_MAIN_CONFLICT',
      message: 'An entry file should not be named as "<name>.js", or you could rename it.',
      data: {
        entry: name_js
      }
    });
  }
  callback(null, pkg);
};

// For now, we only support two dirs
var SUPPORTED_DIRS = [
  'src',
  'dist'
];

// Checks `cortex.directories`
cleaner.check_dirs = function (cwd, pkg, callback) {
  var directories = pkg.directories;
  var dirs = directories
    ? Object.keys(directories)
    : [];

  var supported = dirs.every(function (dir) {
    if (~SUPPORTED_DIRS.indexOf(dir)) {
      return true;
    }

    if (dir === 'css') {
      // However we should tell user to stop using `directories.css`
      callback({
        code: 'NO_SUPPORT_DIR_CSS',
        message: 'Cortex will no longer support `cortex.directories.css` since 4.0.0,\n'
          + 'use `cortex.css` instead.'
      });
    } else {
      callback({
        code: 'NO_SUPPORT_DIR',
        message: '`directories.' + dir + '` is not supported.',
        data: {
          dir: dir
        }
      });
    }

    return false;
  });

  if (!supported) {
    return;
  }

  var items = dirs.map(function (dir) {
    return {
      path: node_path.join(cwd, directories[dir]),
      type: 'isDirectory',
      error: {
        code: 'DIR_NOT_FOUND',
        message: '`directories.' + dir + '` is defined, but not found.',
        data: {
          dir: dir
        }
      }
    };
  });

  // Make sure `directories` and `css` exist
  async.each(items, cleaner._test_path, callback);
};

cleaner._test_path = function (obj, callback) {
  fs.stat(obj.path, function (err, stat) {
    if (err || !stat[obj.type]()) {
      return callback(obj.error);
    }
    callback(null);
  });
};

// Check the existence of cortex.main
// if not exists, pkg.main will be deleted.
cleaner.clean_pkg_main = function (cwd, pkg, callback) {
  var main = pkg.main;
  var parsed;
  if (main) {
    parsed = cleaner._test_file(cwd, main);
    if (!parsed) {
      return callback({
        code: 'CORTEX_MAIN_NOT_FOUND',
        message: '`cortex.main` is defined as "' + main + '", but not found.',
        data: {
          main: main
        }
      });
    }
    return cb(parsed);
  }

  var index = 'index.js';
  var name_js = cleaner._test_file(cwd, pkg.name + '.js');
  // fallback to 'index.js' -> '<name>.js'
  parsed = cleaner._test_file(cwd, index) || name_js;
  cb(parsed);

  function cb (parsed) {
    // `pkg` might has a prototype, so we can't remove a key by deleting them.
    // set it to undefined, `JSON.stringify()` will ignore it.
    pkg.main = parsed;
    callback(null, pkg);
  }
};

cleaner._test_file = function (cwd, file) {
  var origin = file;
  var file = node_path.join(cwd, file);
  try {
    file = require.resolve(file);
  } catch(e) {
    return false;
  }

  // `require.resolve` is really weird that it will change the path of temp directory.
  // The situation below might happen:
  // ```
  // var a = '/var/folders/xxxxxx'
  // var b = require.resolve(a); // -> /private/var/folders/xxxxx.js
  // ```
  var index = file.indexOf(cwd);
  if (~index) {
    // b -> '/var/folders/xxxxx.js'
    file = file.slice(index);
  }

  // Check if file is a `.node` file
  file = cleaner._ban_ext_node(file, origin);
  if (!file) {
    return false;
  }

  // './index.js' -> '/path/to/index.js' -> 'index.js'
  file = node_path.relative(cwd, file);
  return cleaner._standardize(file);
};

cleaner._ban_ext_node = function (parsed, origin) {
  // If the resolved file is a '.node' file
  // - it is originally a '.node' file  -> you take your own responsibility
  // - it is resolved to a '.node' file -> actually it is not allowed in browser -> not found
  return node_path.extname(parsed) === '.node' && node_path.extname(origin) !== '.node'
    ? null
    : parsed;
};

cleaner.clean_pkg_entries = function (cwd, pkg, callback) {
  cleaner._clean_pkg_field(cwd, pkg, 'entries', callback);
};

cleaner.clean_pkg_css = function (cwd, pkg, callback) {
  cleaner._clean_pkg_field(cwd, pkg, 'css', callback);
};

cleaner.clean_pkg_as = function (cwd, pkg, callback) {
  var as_ = pkg['as'];
  if (!as_) {
    return callback(null);
  }

  async.each(Object.keys(as_), function (origin, done) {
    if (cleaner._is_relative(origin)) {
      delete as_[origin];
      return done(null);
    }

    var path = as_[origin];
    if (!cleaner._is_relative(path)) {
      return done(null);
    }

    var found = cleaner._test_file(cwd, path);
    if (!found) {
      return done({
        code: 'AS_NOT_FOUND',
        message: '"' + path + '" of `cortex.as` is not found',
        data: {
          path: path
        }
      });
    }

    if (found.indexOf('..') === 0) {
      return done({
        code: 'AS_OUT_OF_RANGE',
        message: '"' + path + '" of `cortex.as` is out of current package',
        data: {
          path: path
        }
      });
    }

    as_[origin] = found;
    done(null);
  }, callback);
};

cleaner._standardize = function (path) {
  return path.indexOf('..') === 0
    ? path
    : '.' + node_path.sep + path;
};

cleaner._is_relative = function (path) {
  return path.indexOf('../') === 0 || path.indexOf('./') === 0;
};

// @param {string} key
cleaner._clean_pkg_field = function (cwd, pkg, key, callback) {
  var KEY = key.toUpperCase();
  cleaner._expand_items(cwd, pkg[key], function (err, files) {
    if (err) {
      if (err.code === 'NOT_FOUND') {
        return callback({
          code: 'CORTEX_' + KEY + '_NOT_FOUND',
          message: 'The files defined in `cortex.' + key + '`, but not found:\n'
            + err.data.not_found.map(function (file) {
              return '   - ' + file;
            }).join('\n'),
          data: err.data
        });
      }
      return callback(err);
    }
    pkg[key] = files;
    callback(null);
  });
};

cleaner._expand_items = function (cwd, value, callback) {
  if (!value) {
    // #8
    // standardize `pkg.css` and make sure it is always an array.
    return callback(null, []);
  }

  value = util.isArray(value)
    ? value
    : [value];

  var glob_patterns = [];
  var explicit_paths = [];
  value.forEach(function (v) {
    if (~v.indexOf('*')) {
      glob_patterns.push(v);
    } else {
      explicit_paths.push(v);
    }
  });

  var tasks = [];
  var found = [];
  var globbed = [];
  if (glob_patterns.length) {
    tasks.push(function (done) {
      expand(glob_patterns, {
        cwd: cwd
      }, function (err, files) {
        if (err) {
          return done(err);
        }
        globbed = files;
        done(null);
      });
    });
  }

  if (explicit_paths.length) {
    tasks.push(function (done) {
      cleaner._check_multi_exists(cwd, explicit_paths, function (not_found) {
        if (not_found.length) {
          return done({
            code: 'NOT_FOUND',
            data: {
              not_found: not_found
            }
          });
        }
        found = explicit_paths.map(function (path) {
          // './pages/a.js' -> 'pages/a.js'
          return node_path.join('.', path);
        });
        done(null);
      });
    });
  }

  async.parallel(tasks, function (err) {
    // `globbed.length` is larger usually
    callback(err, globbed.concat(found).map(cleaner._standardize));
  });
};

// @param {function(not_found)}
cleaner._check_multi_exists = function (cwd, paths, callback) {
  var not_found = [];
  async.each(paths, function (path, done) {
    var absolute = node_path.join(cwd, path);
    // We only check the existance of the file,
    // because we only gives "enough" hints for people who makes a mistake,
    // but never cares about the situation user deliberately break something.
    fs.exists(absolute, function (exists) {
      if (!exists) {
        not_found.push(path);
      }
      // there will be no errors.
      done(null);
    });
  }, function () {
    callback(not_found);
  });
};
<commit_msg>adds more comments<commit_after>'use strict';

var cleaner = exports;

var util      = require('util');
var async     = require('async');
var fs        = require('fs');
var node_path = require('path');
var expand    = require('fs-expand');

cleaner.clean = function (cwd, pkg, callback) {
  // Silently convert pkg.name to lowercase
  pkg.name = pkg.name.toLowerCase();

  async.each([
    'check_dirs',
    'clean_pkg_css',
    'clean_pkg_main',
    'clean_pkg_entries',
    'clean_pkg_as'
  ], function (task, done) {
    cleaner[task](cwd, pkg, done);

  }, function (err) {
    if (err) {
      return callback(err);
    }
    cleaner.check_entry(pkg, callback);
  });
};

// Cortex must be something to export
cleaner.check_entry = function (pkg, callback) {
  if (!pkg.main && !pkg.css.length && !pkg.entries.length) {
    return callback({
      code: 'CORTEX_NO_ENTRY',
      message: [
        'A package must do something. At least {{bold ONE}} of the 3 fields should be defined and existing:',
        '   - `{{bold cortex.main}}`   : it could be `require()`d',
        '   - `{{bold cortex.css}}`    : the css file(s) could be depent by other packages',
        '   - `{{bold cortex.entries}}`: could be accessed with `facade()`.'
      ].join('\n')
    });
  }

  var name_js = '.' + node_path.sep + pkg.name + '.js';
  if (~pkg.entries.indexOf(name_js)) {
    return callback({
      code: 'CORTEX_MAIN_CONFLICT',
      message: 'An entry file should not be named as "<name>.js", or you could rename it.',
      data: {
        entry: name_js
      }
    });
  }
  callback(null, pkg);
};

// For now, we only support two dirs
var SUPPORTED_DIRS = [
  'src',
  'dist'
];

// Checks `cortex.directories`
cleaner.check_dirs = function (cwd, pkg, callback) {
  var directories = pkg.directories;
  var dirs = directories
    ? Object.keys(directories)
    : [];

  var supported = dirs.every(function (dir) {
    if (~SUPPORTED_DIRS.indexOf(dir)) {
      return true;
    }

    if (dir === 'css') {
      // However we should tell user to stop using `directories.css`
      callback({
        code: 'NO_SUPPORT_DIR_CSS',
        message: 'Cortex will no longer support `cortex.directories.css` since 4.0.0,\n'
          + 'use `cortex.css` instead.'
      });
    } else {
      callback({
        code: 'NO_SUPPORT_DIR',
        message: '`directories.' + dir + '` is not supported.',
        data: {
          dir: dir
        }
      });
    }

    return false;
  });

  if (!supported) {
    return;
  }

  var items = dirs.map(function (dir) {
    return {
      path: node_path.join(cwd, directories[dir]),
      type: 'isDirectory',
      error: {
        code: 'DIR_NOT_FOUND',
        message: '`directories.' + dir + '` is defined, but not found.',
        data: {
          dir: dir
        }
      }
    };
  });

  // Make sure `directories` and `css` exist
  async.each(items, cleaner._test_path, callback);
};

cleaner._test_path = function (obj, callback) {
  fs.stat(obj.path, function (err, stat) {
    if (err || !stat[obj.type]()) {
      return callback(obj.error);
    }
    callback(null);
  });
};

// Check the existence of cortex.main
// if not exists, pkg.main will be deleted.
cleaner.clean_pkg_main = function (cwd, pkg, callback) {
  var main = pkg.main;
  var parsed;
  if (main) {
    parsed = cleaner._test_file(cwd, main);
    if (!parsed) {
      return callback({
        code: 'CORTEX_MAIN_NOT_FOUND',
        message: '`cortex.main` is defined as "' + main + '", but not found.',
        data: {
          main: main
        }
      });
    }
    return cb(parsed);
  }

  var index = 'index.js';
  var name_js = cleaner._test_file(cwd, pkg.name + '.js');
  // fallback to 'index.js' -> '<name>.js'
  parsed = cleaner._test_file(cwd, index) || name_js;
  cb(parsed);

  function cb (parsed) {
    // `pkg` might has a prototype, so we can't remove a key by deleting them.
    // set it to undefined, `JSON.stringify()` will ignore it.
    pkg.main = parsed;
    callback(null, pkg);
  }
};

cleaner._test_file = function (cwd, file) {
  var origin = file;
  var file = node_path.join(cwd, file);
  try {
    file = require.resolve(file);
  } catch(e) {
    return false;
  }

  // `require.resolve` is really weird that it will change the path of temp directory.
  // The situation below might happen:
  // ```
  // var a = '/var/folders/xxxxxx'
  // var b = require.resolve(a); // -> /private/var/folders/xxxxx.js
  // ```
  var index = file.indexOf(cwd);
  if (~index) {
    // b -> '/var/folders/xxxxx.js'
    file = file.slice(index);
  }

  // Check if file is a `.node` file
  file = cleaner._ban_ext_node(file, origin);
  if (!file) {
    return false;
  }

  // './index.js' -> '/path/to/index.js' -> 'index.js'
  file = node_path.relative(cwd, file);
  return cleaner._standardize(file);
};

cleaner._ban_ext_node = function (parsed, origin) {
  // If the resolved file is a '.node' file
  // - it is originally a '.node' file  -> you take your own responsibility
  // - it is resolved to a '.node' file -> actually it is not allowed in browser -> not found
  return node_path.extname(parsed) === '.node' && node_path.extname(origin) !== '.node'
    ? null
    : parsed;
};

cleaner.clean_pkg_entries = function (cwd, pkg, callback) {
  cleaner._clean_pkg_field(cwd, pkg, 'entries', callback);
};

cleaner.clean_pkg_css = function (cwd, pkg, callback) {
  cleaner._clean_pkg_field(cwd, pkg, 'css', callback);
};

// `pkg.as` might be `require.async()`d
// in which situation that it will not be checked by commonjs-walker.
// So, we check it ahead of time.
cleaner.clean_pkg_as = function (cwd, pkg, callback) {
  var as_ = pkg['as'];
  if (!as_) {
    return callback(null);
  }

  async.each(Object.keys(as_), function (origin, done) {
    if (cleaner._is_relative(origin)) {
      delete as_[origin];
      return done(null);
    }

    var path = as_[origin];
    if (!cleaner._is_relative(path)) {
      return done(null);
    }

    var found = cleaner._test_file(cwd, path);
    if (!found) {
      return done({
        code: 'AS_NOT_FOUND',
        message: '"' + path + '" of `cortex.as` is not found',
        data: {
          path: path
        }
      });
    }

    if (found.indexOf('..') === 0) {
      return done({
        code: 'AS_OUT_OF_RANGE',
        message: '"' + path + '" of `cortex.as` is out of current package',
        data: {
          path: path
        }
      });
    }

    as_[origin] = found;
    done(null);
  }, callback);
};

cleaner._standardize = function (path) {
  return path.indexOf('..') === 0
    ? path
    : '.' + node_path.sep + path;
};

cleaner._is_relative = function (path) {
  return path.indexOf('../') === 0 || path.indexOf('./') === 0;
};

// @param {string} key
cleaner._clean_pkg_field = function (cwd, pkg, key, callback) {
  var KEY = key.toUpperCase();
  cleaner._expand_items(cwd, pkg[key], function (err, files) {
    if (err) {
      if (err.code === 'NOT_FOUND') {
        return callback({
          code: 'CORTEX_' + KEY + '_NOT_FOUND',
          message: 'The files defined in `cortex.' + key + '`, but not found:\n'
            + err.data.not_found.map(function (file) {
              return '   - ' + file;
            }).join('\n'),
          data: err.data
        });
      }
      return callback(err);
    }
    pkg[key] = files;
    callback(null);
  });
};

cleaner._expand_items = function (cwd, value, callback) {
  if (!value) {
    // #8
    // standardize `pkg.css` and make sure it is always an array.
    return callback(null, []);
  }

  value = util.isArray(value)
    ? value
    : [value];

  var glob_patterns = [];
  var explicit_paths = [];
  value.forEach(function (v) {
    if (~v.indexOf('*')) {
      glob_patterns.push(v);
    } else {
      explicit_paths.push(v);
    }
  });

  var tasks = [];
  var found = [];
  var globbed = [];
  if (glob_patterns.length) {
    tasks.push(function (done) {
      expand(glob_patterns, {
        cwd: cwd
      }, function (err, files) {
        if (err) {
          return done(err);
        }
        globbed = files;
        done(null);
      });
    });
  }

  if (explicit_paths.length) {
    tasks.push(function (done) {
      cleaner._check_multi_exists(cwd, explicit_paths, function (not_found) {
        if (not_found.length) {
          return done({
            code: 'NOT_FOUND',
            data: {
              not_found: not_found
            }
          });
        }
        found = explicit_paths.map(function (path) {
          // './pages/a.js' -> 'pages/a.js'
          return node_path.join('.', path);
        });
        done(null);
      });
    });
  }

  async.parallel(tasks, function (err) {
    // `globbed.length` is larger usually
    callback(err, globbed.concat(found).map(cleaner._standardize));
  });
};

// @param {function(not_found)}
cleaner._check_multi_exists = function (cwd, paths, callback) {
  var not_found = [];
  async.each(paths, function (path, done) {
    var absolute = node_path.join(cwd, path);
    // We only check the existance of the file,
    // because we only gives "enough" hints for people who makes a mistake,
    // but never cares about the situation user deliberately break something.
    fs.exists(absolute, function (exists) {
      if (!exists) {
        not_found.push(path);
      }
      // there will be no errors.
      done(null);
    });
  }, function () {
    callback(not_found);
  });
};
<|endoftext|>


<commit_before>function HostEventsController (
    $scope,
    $state,
    HostEventService,
    hostEvent,
    OutputStrings
) {
    $scope.processEventStatus = HostEventService.processEventStatus;
    $scope.processResults = processResults;
    $scope.isActiveState = isActiveState;
    $scope.getActiveHostIndex = getActiveHostIndex;
    $scope.closeHostEvent = closeHostEvent;
    $scope.strings = OutputStrings;
    function init () {
        hostEvent.event_name = hostEvent.event;
        $scope.event = _.cloneDeep(hostEvent);

        // grab standard out & standard error if present from the host
        // event's 'res' object, for things like Ansible modules. Small
        // wrinkle in this implementation is that the stdout/stderr tabs
        // should be shown if the `res` object has stdout/stderr keys, even
        // if they're a blank string. The presence of these keys is
        // potentially significant to a user.
        if (_.has(hostEvent.event_data, 'task_action')) {
            $scope.module_name = hostEvent.event_data.task_action;
        } else if (!_.has(hostEvent.event_data, 'task_action')) {
            $scope.module_name = 'No result found';
        }

        if (_.has(hostEvent.event_data, 'res.stdout')) {
            if (hostEvent.event_data.res.stdout === '') {
                $scope.stdout = ' ';
            } else {
                $scope.stdout = hostEvent.event_data.res.stdout;
            }
        }

        if (_.has(hostEvent.event_data, 'res.stderr')) {
            if (hostEvent.event_data.res.stderr === '') {
                $scope.stderr = ' ';
            } else {
                $scope.stderr = hostEvent.event_data.res.stderr;
            }
        }

        if (_.has(hostEvent.event_data, 'res')) {
            $scope.json = hostEvent.event_data.res;
        }

        if ($scope.module_name === 'debug' &&
            _.has(hostEvent.event_data, 'res.result.stdout')) {
            $scope.stdout = hostEvent.event_data.res.result.stdout;
        }
        if ($scope.module_name === 'yum' &&
            _.has(hostEvent.event_data, 'res.results') &&
            _.isArray(hostEvent.event_data.res.results)) {
            const event = hostEvent.event_data.res.results;
            $scope.stdout = event[0];// eslint-disable-line prefer-destructuring
        }
        // instantiate Codemirror
        if ($state.current.name === 'output.host-event.json') {
            try {
                if (_.has(hostEvent.event_data, 'res')) {
                    initCodeMirror(
                        'HostEvent-codemirror',
                        JSON.stringify($scope.json, null, 4),
                        { name: 'javascript', json: true }
                    );
                    resize();
                } else {
                    $scope.no_json = true;
                }
            } catch (err) {
                // element with id HostEvent-codemirror is not the view
                // controlled by this instance of HostEventController
            }
        } else if ($state.current.name === 'output.host-event.stdout') {
            try {
                resize();
            } catch (err) {
                // element with id HostEvent-codemirror is not the view
                // controlled by this instance of HostEventController
            }
        } else if ($state.current.name === 'output.host-event.stderr') {
            try {
                resize();
            } catch (err) {
                // element with id HostEvent-codemirror is not the view
                // controlled by this instance of HostEventController
            }
        }
        $('#HostEvent').modal('show');
        $('.modal-content').resizable({
            minHeight: 523,
            minWidth: 600
        });
        $('.modal-dialog').draggable({
            cancel: '.HostEvent-view--container'
        });

        function resize () {
            if ($state.current.name === 'output.host-event.json') {
                const editor = $('.CodeMirror')[0].CodeMirror;
                const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120;
                editor.setSize('100%', height);
            } else if ($state.current.name === 'output.host-event.stdout' || $state.current.name === 'output.host-event.stderr') {
                const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120;
                $('.HostEvent-stdout').width('100%');
                $('.HostEvent-stdout').height(height);
                $('.HostEvent-stdoutContainer').height(height);
                $('.HostEvent-numberColumnPreload').height(height);
            }
        }

        $('.modal-dialog').on('resize', resize);

        $('#HostEvent').on('hidden.bs.modal', $scope.closeHostEvent);
    }

    function processResults (value) {
        if (typeof value === 'object') {
            return false;
        }
        return true;
    }

    function initCodeMirror (el, data, mode) {
        const container = document.getElementById(el);
        const options = {};
        options.lineNumbers = true;
        options.mode = mode;
        options.readOnly = true;
        options.scrollbarStyle = null;
        const editor = CodeMirror.fromTextArea(// eslint-disable-line no-undef
            container,
            options
        );
        editor.setSize('100%', 200);
        editor.getDoc().setValue(data);
    }

    function isActiveState (name) {
        return $state.current.name === name;
    }

    function getActiveHostIndex () {
        function hostResultfilter (obj) {
            return obj.id === $scope.event.id;
        }
        const result = $scope.hostResults.filter(hostResultfilter);
        return $scope.hostResults.indexOf(result[0]);
    }

    function closeHostEvent () {
        // Unbind the listener so it doesn't fire when we close the modal via navigation
        $('#HostEvent').off('hidden.bs.modal');
        $('#HostEvent').modal('hide');
        $state.go('output');
    }
    $scope.init = init;
    $scope.init();
}

HostEventsController.$inject = [
    '$scope',
    '$state',
    'HostEventService',
    'hostEvent',
    'OutputStrings'
];

module.exports = HostEventsController;
<commit_msg>santize host detail modal stderr and stdout<commit_after>function HostEventsController (
    $scope,
    $state,
    $filter,
    HostEventService,
    hostEvent,
    OutputStrings
) {
    $scope.processEventStatus = HostEventService.processEventStatus;
    $scope.processResults = processResults;
    $scope.isActiveState = isActiveState;
    $scope.getActiveHostIndex = getActiveHostIndex;
    $scope.closeHostEvent = closeHostEvent;
    $scope.strings = OutputStrings;

    const sanitize = $filter('sanitize');

    function init () {
        hostEvent.event_name = hostEvent.event;
        $scope.event = _.cloneDeep(hostEvent);

        // grab standard out & standard error if present from the host
        // event's 'res' object, for things like Ansible modules. Small
        // wrinkle in this implementation is that the stdout/stderr tabs
        // should be shown if the `res` object has stdout/stderr keys, even
        // if they're a blank string. The presence of these keys is
        // potentially significant to a user.
        if (_.has(hostEvent.event_data, 'task_action')) {
            $scope.module_name = hostEvent.event_data.task_action;
        } else if (!_.has(hostEvent.event_data, 'task_action')) {
            $scope.module_name = 'No result found';
        }

        if (_.has(hostEvent.event_data, 'res.stdout')) {
            if (hostEvent.event_data.res.stdout === '') {
                $scope.stdout = ' ';
            } else {
                $scope.stdout = sanitize(hostEvent.event_data.res.stdout);
            }
        }

        if (_.has(hostEvent.event_data, 'res.stderr')) {
            if (hostEvent.event_data.res.stderr === '') {
                $scope.stderr = ' ';
            } else {
                $scope.stderr = sanitize(hostEvent.event_data.res.stderr);
            }
        }

        if (_.has(hostEvent.event_data, 'res')) {
            $scope.json = hostEvent.event_data.res;
        }

        if ($scope.module_name === 'debug' &&
            _.has(hostEvent.event_data, 'res.result.stdout')) {
            $scope.stdout = sanitize(hostEvent.event_data.res.result.stdout);
        }
        if ($scope.module_name === 'yum' &&
            _.has(hostEvent.event_data, 'res.results') &&
            _.isArray(hostEvent.event_data.res.results)) {
            const event = hostEvent.event_data.res.results;
            $scope.stdout = sanitize(event[0]);// eslint-disable-line prefer-destructuring
        }
        // instantiate Codemirror
        if ($state.current.name === 'output.host-event.json') {
            try {
                if (_.has(hostEvent.event_data, 'res')) {
                    initCodeMirror(
                        'HostEvent-codemirror',
                        JSON.stringify($scope.json, null, 4),
                        { name: 'javascript', json: true }
                    );
                    resize();
                } else {
                    $scope.no_json = true;
                }
            } catch (err) {
                // element with id HostEvent-codemirror is not the view
                // controlled by this instance of HostEventController
            }
        } else if ($state.current.name === 'output.host-event.stdout') {
            try {
                resize();
            } catch (err) {
                // element with id HostEvent-codemirror is not the view
                // controlled by this instance of HostEventController
            }
        } else if ($state.current.name === 'output.host-event.stderr') {
            try {
                resize();
            } catch (err) {
                // element with id HostEvent-codemirror is not the view
                // controlled by this instance of HostEventController
            }
        }
        $('#HostEvent').modal('show');
        $('.modal-content').resizable({
            minHeight: 523,
            minWidth: 600
        });
        $('.modal-dialog').draggable({
            cancel: '.HostEvent-view--container'
        });

        function resize () {
            if ($state.current.name === 'output.host-event.json') {
                const editor = $('.CodeMirror')[0].CodeMirror;
                const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120;
                editor.setSize('100%', height);
            } else if ($state.current.name === 'output.host-event.stdout' || $state.current.name === 'output.host-event.stderr') {
                const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120;
                $('.HostEvent-stdout').width('100%');
                $('.HostEvent-stdout').height(height);
                $('.HostEvent-stdoutContainer').height(height);
                $('.HostEvent-numberColumnPreload').height(height);
            }
        }

        $('.modal-dialog').on('resize', resize);

        $('#HostEvent').on('hidden.bs.modal', $scope.closeHostEvent);
    }

    function processResults (value) {
        if (typeof value === 'object') {
            return false;
        }
        return true;
    }

    function initCodeMirror (el, data, mode) {
        const container = document.getElementById(el);
        const options = {};
        options.lineNumbers = true;
        options.mode = mode;
        options.readOnly = true;
        options.scrollbarStyle = null;
        const editor = CodeMirror.fromTextArea(// eslint-disable-line no-undef
            container,
            options
        );
        editor.setSize('100%', 200);
        editor.getDoc().setValue(data);
    }

    function isActiveState (name) {
        return $state.current.name === name;
    }

    function getActiveHostIndex () {
        function hostResultfilter (obj) {
            return obj.id === $scope.event.id;
        }
        const result = $scope.hostResults.filter(hostResultfilter);
        return $scope.hostResults.indexOf(result[0]);
    }

    function closeHostEvent () {
        // Unbind the listener so it doesn't fire when we close the modal via navigation
        $('#HostEvent').off('hidden.bs.modal');
        $('#HostEvent').modal('hide');
        $state.go('output');
    }
    $scope.init = init;
    $scope.init();
}

HostEventsController.$inject = [
    '$scope',
    '$state',
    '$filter',
    'HostEventService',
    'hostEvent',
    'OutputStrings'
];

module.exports = HostEventsController;
<|endoftext|>