<commit_before>// The driver maintains the state of the application and drives everything.
// (Well, it knows about everything except for routing)
// Most everything else is just stateless
// This is similar to Redux except more flexible for faster development
import Printify from '../lib/Printify';
import Byol from './Byol';
import _ from 'lodash';
import Stellarify from '../lib/Stellarify';
import BigNumber from 'bignumber.js';
BigNumber.config({ EXPONENTIAL_AT: 100 });

// Spoonfed Stellar-SDK: Super easy to use higher level Stellar-Sdk functions
// Simplifies the objects to what is necessary. Listens to updates automagically.
// It's in the same file as the driver because the driver is the only one that
// should ever use the spoon.
const MagicSpoon = {
  async Account(Server, keypair, onUpdate) {
    let sdkAccount = await Server.loadAccount(keypair.accountId())
    sdkAccount.sign = transaction => {
      transaction.sign(keypair);
    };

    // Expects StellarSdk.Asset
    // Returns null if there is no trust
    // Returns string of balance if exists
    sdkAccount.getBalance = (targetAsset) => {
      let targetBalance = null;
      if (targetAsset.isNative()) {
        _.each(sdkAccount.balances, balance => {
          if (balance.asset_type === 'native') {
            targetBalance = balance.balance;
          }
        });
      } else {
        _.each(sdkAccount.balances, balance => {
          if (balance.asset_code === targetAsset.getCode() && balance.asset_issuer === targetAsset.getIssuer()) {
            targetBalance = balance.balance;
          }
        });
      }
      return targetBalance;
    }

    let accountEventsClose = Server.accounts().accountId(keypair.accountId()).stream({
      onmessage: res => {
        let updated = false;
        if (!_.isEqual(sdkAccount.balances, res.balances)) {
          sdkAccount.balances = res.balances;
          updated = true;
        }

        if (updated) {
          onUpdate();
        }
      }
    });

    sdkAccount.offers = {};

    // Horizon offers for account doesn't return us updates. So we will have to manually update it.
    // We won't miss any offers assuming that the user only updates their offers through the client
    // with just one window open at a time
    sdkAccount.updateOffers = () => {
      return Server.offers('accounts', keypair.accountId())
        .limit(100) // TODO: Keep iterating through next() to show more than 100 offers
        .call()
        .then(res => {
          let newOffers = {};
          _.each(res.records, offer => {
            newOffers[offer.id] = offer;
          });
          sdkAccount.offers = newOffers;
          onUpdate();
          return null;
        });
    }
    sdkAccount.updateOffers();

    sdkAccount.close = () => {
      accountEventsClose();
    };

    return sdkAccount;
  },
  Orderbook(Server, baseBuying, counterSelling, onUpdate) {
    // Orderbook is an object that keeps track of the orderbook for you.
    // All the driver needs to do is remember to call the close function

    this.ready = false;
    const initialOrderbook = Server.orderbook(baseBuying, counterSelling).call()
      .then((res) => {
        this.asks = res.asks;
        this.bids = res.bids;
        this.baseBuying = baseBuying;
        this.counterSelling = counterSelling;
        this.ready = true;
        onUpdate();

      });
    let streamingOrderbookClose = Server.orderbook(baseBuying, counterSelling)
      .stream({
        onmessage: res => {
          let updated = false;
          if (!_.isEqual(this.bids, res.bids)) {
            this.bids = res.bids;
            updated = true;
          }
          if (!_.isEqual(this.asks, res.asks)) {
            this.asks = res.asks;
            updated = true;
          }
          if (updated) {
            onUpdate();
          }
        }
      })

    const orderbookTrades = Server.orderbook(baseBuying, counterSelling).trades().call()
      .then((res) => {
        this.trades = _.map(res.records, (trade, index) => {
          return [new Date(trade.created_at).getTime(), new BigNumber(trade.bought_amount).dividedBy(trade.sold_amount).toNumber()];
        });
        onUpdate();
      });

    this.close = streamingOrderbookClose;
    // TODO: Close
  },

  // opts.baseBuying -- StellarSdk.Asset (example: XLM)
  // opts.counterSelling -- StellarSdk.Asset (example: USD)
  // opts.price -- Exchange ratio selling/buying
  // opts.amount -- Here, it's relative to the base (JS-sdk does: Total amount selling)
  // opts.type -- String of either 'buy' or 'sell' (relative to base currency)
  createOffer(Server, spoonAccount, side, opts) {
    let sdkBuying;
    let sdkSelling;
    let sdkPrice;
    let sdkAmount;

    const bigOptsPrice = new BigNumber(opts.price).toPrecision(15);
    const bigOptsAmount = new BigNumber(opts.amount).toPrecision(15);

    console.log(`Creating *${side}* offer at price ${opts.price}`);
    if (side === 'buy') {
      sdkBuying = opts.baseBuying; // ex: lumens
      sdkSelling = opts.counterSelling; // ex: USD
      sdkPrice = new BigNumber(1).dividedBy(bigOptsPrice);
      sdkAmount = new BigNumber(bigOptsAmount).times(bigOptsPrice).toFixed(7);
    } else if (side === 'sell') {
      sdkBuying = opts.counterSelling; // ex: USD
      sdkSelling = opts.baseBuying; // ex: lumens
      sdkPrice = new BigNumber(bigOptsPrice);
      sdkAmount = new BigNumber(bigOptsAmount).toFixed(7);
    } else {
      throw new Error(`Invalid side ${side}`);
    }

    const operationOpts = {
      buying: sdkBuying,
      selling: sdkSelling,
      amount: String(sdkAmount),
      price: String(sdkPrice),
      offerId: 0, // 0 for new offer
    };
    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.manageOffer(operationOpts))
      .build();
    spoonAccount.sign(transaction);
    return Server.submitTransaction(transaction)
      .then(res => {
        console.log('Offer create success');
        console.log(res)
        spoonAccount.updateOffers(); // Just to be doubly sure
        return;
      })
  },
  async sendPayment(Server, spoonAccount,  opts) {
    const operationOpts = {
      destination: opts.destination,
      asset: opts.asset,
      amount: opts.amount,
    };

    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.payment(operationOpts))
      // .addMemo(StellarSdk.Memo.text('hello'))
      .build();
    spoonAccount.sign(transaction);

    const transactionResult = await Server.submitTransaction(transaction);
    return transactionResult;
  },
  changeTrust(Server, spoonAccount, opts) {
    let sdkLimit;
    if (typeof opts.limit === 'string' || opts.limit instanceof String) {
      sdkLimit = opts.limit;
    } else if (opts.limit !== undefined) {
      throw new Error('changeTrust opts.limit must be a string');
    }

    const operationOpts = {
      asset: opts.asset,
      limit: sdkLimit,
    };

    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.changeTrust(operationOpts))
      .build();
    spoonAccount.sign(transaction);
    const transactionResult = Server.submitTransaction(transaction);
    return transactionResult;
  },
  async removeOffer(Server, spoonAccount, offerId) {
    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.manageOffer({
        buying: StellarSdk.Asset.native(),
        selling: new StellarSdk.Asset('REMOVE', spoonAccount.accountId()),
        amount: '0',
        price: '1',
        offerId,
      }))
      .build();
    spoonAccount.sign(transaction);
    const transactionResult = await Server.submitTransaction(transaction);
    spoonAccount.updateOffers();
    return transactionResult;
  },
};

// Using old school "classes" because I'm old school and it's simpler to
// understand. I may use the ES6 form later though.
function Driver(opts) {
  this.Server = new StellarSdk.Server(opts.horizonUrl); // Should never change
  this.Server.serverUrl = opts.horizonUrl;

  const byol = new Byol();

  const availableEvents = [
    'session',
    'orderbook',
    'orderbookPricePick',
    'send',
  ];
  const trigger = {};
  availableEvents.forEach((eventName) => {
    this[`listen${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`] = cb => byol.listen(eventName, cb);
    this[`unlisten${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`] = id => byol.unlisten(eventName, id);
    trigger[eventName] = (opts) => byol.trigger(eventName, opts);
  });

  // ----- Initializations above this line -----
  // Only the driver should change the session.
  this.session = {
    state: 'out',
    setupError: false, // Couldn't find account
    account: null, // MagicSpoon.Account instance
  };
  // this.session = {
  //   state: 'in',
  //   setupError: false, // Couldn't find account
  //   account: {
  //   }, // MagicSpoon.Account instance
  // };
  // Due to a bug in horizon where it doesn't update offers for accounts, we have to manually check
  // It shouldn't cause too much of an overhead
  let forceUpdateAccountOffers = () => {
    let updateFn = _.get(this.session, 'account.updateOffers');
    if (updateFn) {
      updateFn();
    }
  }

  this.send = {
    init: () => {
      this.send.state = 'setup'; // 'setup' | 'pending' | 'error' | 'success'
      this.send.step = 1; // Starts at 1. Natural indexing corresponds to the step numbers
    },

    // Constraint: Each step is allowed to safely assume that the previous steps are finished

    // Step state is initialized by the reset functions
    step1: {},
    resetStep1: () => {
      this.send.step1 = {
        accountId: '',
      };
    },

    step2: {},
    resetStep2: () => {
      this.send.step2 = {
        availableAssets: [
          new StellarSdk.Asset.native(),
        ],
        asset: null,
        selectedIndex: null,
      };
    },

    step3: {},
    resetStep3: () => {
      this.send.step3 = {
        amount: '',
      };
    },

    accountId: '',
    handlers: {
      updateAccountId: (e) => {
        this.send.accountId = e.target.value;
        trigger.send();
      },
      step1Edit: () => {
        this.send.step = 1;
        this.send.resetStep2();
        this.send.resetStep3();
        trigger.send();
      },
      step1Next: () => {
        this.send.step = 2;
        trigger.send();
      },

      step2Edit: () => {
        this.send.step = 2;
        this.send.resetStep3();
        trigger.send();
      },
      step2PickAsset: (index) => {
        // Step 2 doesn't have a next button because this acts as the next button
        this.send.step2.asset = this.send.step2.availableAssets[index];
        this.send.step2.selectedIndex = index;
        this.send.step = 3;
        trigger.send();
      },

      step3Edit: () => {
        this.send.step = 3;
        trigger.send();
      },
      updateAmount: (e) => {
        this.send.step3.amount = e.target.value;
        trigger.send();
      },
      step3Next: () => {
        this.send.step = 4;
        trigger.send();
      },
      submit: async () => {
        this.send.state = 'pending';
        trigger.send();
        let result;
        try {
          result = await MagicSpoon.sendPayment(this.Server, this.session.account, {
            destination: this.send.accountId,
            asset: this.send.step2.asset,
            amount: this.send.step3.amount,
          });
          this.send.txId = result.hash;
          this.send.state = 'success';
        } catch(err) {
          this.send.state = 'error';
          if (err instanceof Error) {
            this.send.errorDetails = err.message;
          } else {
            this.send.errorDetails = JSON.stringify(err, null, 2);
          }
        }
        trigger.send();
      },
      reset: () => {
        this.send.resetAll();
        trigger.send();
      }
    },

    resetAll: () => {
      this.send.init();
      this.send.resetStep1();
      this.send.resetStep2();
      this.send.resetStep3();
    },
  };

  this.send.resetAll();

  // TODO: Possible (rare) race condition since ready: false can mean either: 1. no pair picked, 2. Loading orderbook from horizon
  this.orderbook = {
    ready: false,
  };

  this.handlers = {
    logIn: async (secretKey) => {
      let keypair;
      try {
        keypair = StellarSdk.Keypair.fromSeed(secretKey);
      } catch (e) {
        console.log('Invalid secret key');
        return;
      }
      this.session.setupError = false;
      this.session.state = 'loading';
      trigger.session();

      try {
        this.session.account = await MagicSpoon.Account(this.Server, keypair, () => {
          trigger.session();
        });
        this.session.state = 'in';
        trigger.session();
      } catch (e) {
        this.session.state = 'out';
        this.session.setupError = true;
        trigger.session();
      }
    },
    createOffer: async (side, opts) => {
      return MagicSpoon.createOffer(this.Server, this.session.account, side, _.assign(opts, {
        baseBuying: this.orderbook.baseBuying,
        counterSelling: this.orderbook.counterSelling,
      }));
    },
    addTrust: async (code, issuer) => {
      // For simplicity, currently only adds max trust line
      return MagicSpoon.changeTrust(this.Server, this.session.account, {
        asset: new StellarSdk.Asset(code, issuer),
      })
    },
    removeTrust: async (code, issuer) => {
      return await MagicSpoon.changeTrust(this.Server, this.session.account, {
        asset: new StellarSdk.Asset(code, issuer),
        limit: '0',
      })
    },
    removeOffer: async (offerId) => {
      return MagicSpoon.removeOffer(this.Server, this.session.account, offerId);
    },
    orderbookPricePick: (price) => {
      trigger.orderbookPricePick({
        price,
      })
    },
    setOrderbook: (baseBuying, counterSelling) => {
      // If orderbook is already set, then this is a no-op
      // Expects baseBuying and counterSelling to StellarSdk.Asset objects
      if (this.orderbook.ready && this.orderbook.baseBuying.equals(baseBuying) && this.orderbook.counterSelling.equals(counterSelling)) {
        return;
      }

      if (this.orderbook.close) {
        this.orderbook.close();
      }
      this.orderbook = new MagicSpoon.Orderbook(this.Server, baseBuying, counterSelling, () => {
        trigger.orderbook();
        forceUpdateAccountOffers();
      });
    }
  };
}

export default Driver;
<commit_msg>Fix state resetting in Send<commit_after>// The driver maintains the state of the application and drives everything.
// (Well, it knows about everything except for routing)
// Most everything else is just stateless
// This is similar to Redux except more flexible for faster development
import Printify from '../lib/Printify';
import Byol from './Byol';
import _ from 'lodash';
import Stellarify from '../lib/Stellarify';
import BigNumber from 'bignumber.js';
BigNumber.config({ EXPONENTIAL_AT: 100 });

// Spoonfed Stellar-SDK: Super easy to use higher level Stellar-Sdk functions
// Simplifies the objects to what is necessary. Listens to updates automagically.
// It's in the same file as the driver because the driver is the only one that
// should ever use the spoon.
const MagicSpoon = {
  async Account(Server, keypair, onUpdate) {
    let sdkAccount = await Server.loadAccount(keypair.accountId())
    sdkAccount.sign = transaction => {
      transaction.sign(keypair);
    };

    // Expects StellarSdk.Asset
    // Returns null if there is no trust
    // Returns string of balance if exists
    sdkAccount.getBalance = (targetAsset) => {
      let targetBalance = null;
      if (targetAsset.isNative()) {
        _.each(sdkAccount.balances, balance => {
          if (balance.asset_type === 'native') {
            targetBalance = balance.balance;
          }
        });
      } else {
        _.each(sdkAccount.balances, balance => {
          if (balance.asset_code === targetAsset.getCode() && balance.asset_issuer === targetAsset.getIssuer()) {
            targetBalance = balance.balance;
          }
        });
      }
      return targetBalance;
    }

    let accountEventsClose = Server.accounts().accountId(keypair.accountId()).stream({
      onmessage: res => {
        let updated = false;
        if (!_.isEqual(sdkAccount.balances, res.balances)) {
          sdkAccount.balances = res.balances;
          updated = true;
        }

        if (updated) {
          onUpdate();
        }
      }
    });

    sdkAccount.offers = {};

    // Horizon offers for account doesn't return us updates. So we will have to manually update it.
    // We won't miss any offers assuming that the user only updates their offers through the client
    // with just one window open at a time
    sdkAccount.updateOffers = () => {
      return Server.offers('accounts', keypair.accountId())
        .limit(100) // TODO: Keep iterating through next() to show more than 100 offers
        .call()
        .then(res => {
          let newOffers = {};
          _.each(res.records, offer => {
            newOffers[offer.id] = offer;
          });
          sdkAccount.offers = newOffers;
          onUpdate();
          return null;
        });
    }
    sdkAccount.updateOffers();

    sdkAccount.close = () => {
      accountEventsClose();
    };

    return sdkAccount;
  },
  Orderbook(Server, baseBuying, counterSelling, onUpdate) {
    // Orderbook is an object that keeps track of the orderbook for you.
    // All the driver needs to do is remember to call the close function

    this.ready = false;
    const initialOrderbook = Server.orderbook(baseBuying, counterSelling).call()
      .then((res) => {
        this.asks = res.asks;
        this.bids = res.bids;
        this.baseBuying = baseBuying;
        this.counterSelling = counterSelling;
        this.ready = true;
        onUpdate();

      });
    let streamingOrderbookClose = Server.orderbook(baseBuying, counterSelling)
      .stream({
        onmessage: res => {
          let updated = false;
          if (!_.isEqual(this.bids, res.bids)) {
            this.bids = res.bids;
            updated = true;
          }
          if (!_.isEqual(this.asks, res.asks)) {
            this.asks = res.asks;
            updated = true;
          }
          if (updated) {
            onUpdate();
          }
        }
      })

    const orderbookTrades = Server.orderbook(baseBuying, counterSelling).trades().call()
      .then((res) => {
        this.trades = _.map(res.records, (trade, index) => {
          return [new Date(trade.created_at).getTime(), new BigNumber(trade.bought_amount).dividedBy(trade.sold_amount).toNumber()];
        });
        onUpdate();
      });

    this.close = streamingOrderbookClose;
    // TODO: Close
  },

  // opts.baseBuying -- StellarSdk.Asset (example: XLM)
  // opts.counterSelling -- StellarSdk.Asset (example: USD)
  // opts.price -- Exchange ratio selling/buying
  // opts.amount -- Here, it's relative to the base (JS-sdk does: Total amount selling)
  // opts.type -- String of either 'buy' or 'sell' (relative to base currency)
  createOffer(Server, spoonAccount, side, opts) {
    let sdkBuying;
    let sdkSelling;
    let sdkPrice;
    let sdkAmount;

    const bigOptsPrice = new BigNumber(opts.price).toPrecision(15);
    const bigOptsAmount = new BigNumber(opts.amount).toPrecision(15);

    console.log(`Creating *${side}* offer at price ${opts.price}`);
    if (side === 'buy') {
      sdkBuying = opts.baseBuying; // ex: lumens
      sdkSelling = opts.counterSelling; // ex: USD
      sdkPrice = new BigNumber(1).dividedBy(bigOptsPrice);
      sdkAmount = new BigNumber(bigOptsAmount).times(bigOptsPrice).toFixed(7);
    } else if (side === 'sell') {
      sdkBuying = opts.counterSelling; // ex: USD
      sdkSelling = opts.baseBuying; // ex: lumens
      sdkPrice = new BigNumber(bigOptsPrice);
      sdkAmount = new BigNumber(bigOptsAmount).toFixed(7);
    } else {
      throw new Error(`Invalid side ${side}`);
    }

    const operationOpts = {
      buying: sdkBuying,
      selling: sdkSelling,
      amount: String(sdkAmount),
      price: String(sdkPrice),
      offerId: 0, // 0 for new offer
    };
    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.manageOffer(operationOpts))
      .build();
    spoonAccount.sign(transaction);
    return Server.submitTransaction(transaction)
      .then(res => {
        console.log('Offer create success');
        console.log(res)
        spoonAccount.updateOffers(); // Just to be doubly sure
        return;
      })
  },
  async sendPayment(Server, spoonAccount,  opts) {
    const operationOpts = {
      destination: opts.destination,
      asset: opts.asset,
      amount: opts.amount,
    };

    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.payment(operationOpts))
      // .addMemo(StellarSdk.Memo.text('hello'))
      .build();
    spoonAccount.sign(transaction);

    const transactionResult = await Server.submitTransaction(transaction);
    return transactionResult;
  },
  changeTrust(Server, spoonAccount, opts) {
    let sdkLimit;
    if (typeof opts.limit === 'string' || opts.limit instanceof String) {
      sdkLimit = opts.limit;
    } else if (opts.limit !== undefined) {
      throw new Error('changeTrust opts.limit must be a string');
    }

    const operationOpts = {
      asset: opts.asset,
      limit: sdkLimit,
    };

    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.changeTrust(operationOpts))
      .build();
    spoonAccount.sign(transaction);
    const transactionResult = Server.submitTransaction(transaction);
    return transactionResult;
  },
  async removeOffer(Server, spoonAccount, offerId) {
    const transaction = new StellarSdk.TransactionBuilder(spoonAccount)
      .addOperation(StellarSdk.Operation.manageOffer({
        buying: StellarSdk.Asset.native(),
        selling: new StellarSdk.Asset('REMOVE', spoonAccount.accountId()),
        amount: '0',
        price: '1',
        offerId,
      }))
      .build();
    spoonAccount.sign(transaction);
    const transactionResult = await Server.submitTransaction(transaction);
    spoonAccount.updateOffers();
    return transactionResult;
  },
};

// Using old school "classes" because I'm old school and it's simpler to
// understand. I may use the ES6 form later though.
function Driver(opts) {
  this.Server = new StellarSdk.Server(opts.horizonUrl); // Should never change
  this.Server.serverUrl = opts.horizonUrl;

  const byol = new Byol();

  const availableEvents = [
    'session',
    'orderbook',
    'orderbookPricePick',
    'send',
  ];
  const trigger = {};
  availableEvents.forEach((eventName) => {
    this[`listen${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`] = cb => byol.listen(eventName, cb);
    this[`unlisten${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`] = id => byol.unlisten(eventName, id);
    trigger[eventName] = (opts) => byol.trigger(eventName, opts);
  });

  // ----- Initializations above this line -----
  // Only the driver should change the session.
  this.session = {
    state: 'out',
    setupError: false, // Couldn't find account
    account: null, // MagicSpoon.Account instance
  };
  // this.session = {
  //   state: 'in',
  //   setupError: false, // Couldn't find account
  //   account: {
  //   }, // MagicSpoon.Account instance
  // };
  // Due to a bug in horizon where it doesn't update offers for accounts, we have to manually check
  // It shouldn't cause too much of an overhead
  let forceUpdateAccountOffers = () => {
    let updateFn = _.get(this.session, 'account.updateOffers');
    if (updateFn) {
      updateFn();
    }
  }

  this.send = {
    init: () => {
      this.send.state = 'setup'; // 'setup' | 'pending' | 'error' | 'success'
      this.send.step = 1; // Starts at 1. Natural indexing corresponds to the step numbers
    },

    // Constraint: Each step is allowed to safely assume that the previous steps are finished

    // Step state is initialized by the reset functions
    resetStep1: () => {
      this.send.accountId = '';
    },

    step2: {},
    resetStep2: () => {
      this.send.step2 = {
        availableAssets: [
          new StellarSdk.Asset.native(),
        ],
        asset: null,
        selectedIndex: null,
      };
    },

    step3: {},
    resetStep3: () => {
      this.send.step3 = {
        amount: '',
      };
    },

    accountId: '',
    handlers: {
      updateAccountId: (e) => {
        this.send.accountId = e.target.value;
        trigger.send();
      },
      step1Edit: () => {
        this.send.step = 1;
        this.send.resetStep2();
        this.send.resetStep3();
        trigger.send();
      },
      step1Next: () => {
        this.send.step = 2;
        trigger.send();
      },

      step2Edit: () => {
        this.send.step = 2;
        this.send.resetStep3();
        trigger.send();
      },
      step2PickAsset: (index) => {
        // Step 2 doesn't have a next button because this acts as the next button
        this.send.step2.asset = this.send.step2.availableAssets[index];
        this.send.step2.selectedIndex = index;
        this.send.step = 3;
        trigger.send();
      },

      step3Edit: () => {
        this.send.step = 3;
        trigger.send();
      },
      updateAmount: (e) => {
        this.send.step3.amount = e.target.value;
        trigger.send();
      },
      step3Next: () => {
        this.send.step = 4;
        trigger.send();
      },
      submit: async () => {
        this.send.state = 'pending';
        trigger.send();
        let result;
        try {
          result = await MagicSpoon.sendPayment(this.Server, this.session.account, {
            destination: this.send.accountId,
            asset: this.send.step2.asset,
            amount: this.send.step3.amount,
          });
          this.send.txId = result.hash;
          this.send.state = 'success';
        } catch(err) {
          this.send.state = 'error';
          if (err instanceof Error) {
            this.send.errorDetails = err.message;
          } else {
            this.send.errorDetails = JSON.stringify(err, null, 2);
          }
        }
        trigger.send();
      },
      reset: () => {
        this.send.resetAll();
        trigger.send();
      }
    },

    resetAll: () => {
      this.send.init();
      this.send.resetStep1();
      this.send.resetStep2();
      this.send.resetStep3();
    },
  };

  this.send.resetAll();

  // TODO: Possible (rare) race condition since ready: false can mean either: 1. no pair picked, 2. Loading orderbook from horizon
  this.orderbook = {
    ready: false,
  };

  this.handlers = {
    logIn: async (secretKey) => {
      let keypair;
      try {
        keypair = StellarSdk.Keypair.fromSeed(secretKey);
      } catch (e) {
        console.log('Invalid secret key');
        return;
      }
      this.session.setupError = false;
      this.session.state = 'loading';
      trigger.session();

      try {
        this.session.account = await MagicSpoon.Account(this.Server, keypair, () => {
          trigger.session();
        });
        this.session.state = 'in';
        trigger.session();
      } catch (e) {
        this.session.state = 'out';
        this.session.setupError = true;
        trigger.session();
      }
    },
    createOffer: async (side, opts) => {
      return MagicSpoon.createOffer(this.Server, this.session.account, side, _.assign(opts, {
        baseBuying: this.orderbook.baseBuying,
        counterSelling: this.orderbook.counterSelling,
      }));
    },
    addTrust: async (code, issuer) => {
      // For simplicity, currently only adds max trust line
      return MagicSpoon.changeTrust(this.Server, this.session.account, {
        asset: new StellarSdk.Asset(code, issuer),
      })
    },
    removeTrust: async (code, issuer) => {
      return await MagicSpoon.changeTrust(this.Server, this.session.account, {
        asset: new StellarSdk.Asset(code, issuer),
        limit: '0',
      })
    },
    removeOffer: async (offerId) => {
      return MagicSpoon.removeOffer(this.Server, this.session.account, offerId);
    },
    orderbookPricePick: (price) => {
      trigger.orderbookPricePick({
        price,
      })
    },
    setOrderbook: (baseBuying, counterSelling) => {
      // If orderbook is already set, then this is a no-op
      // Expects baseBuying and counterSelling to StellarSdk.Asset objects
      if (this.orderbook.ready && this.orderbook.baseBuying.equals(baseBuying) && this.orderbook.counterSelling.equals(counterSelling)) {
        return;
      }

      if (this.orderbook.close) {
        this.orderbook.close();
      }
      this.orderbook = new MagicSpoon.Orderbook(this.Server, baseBuying, counterSelling, () => {
        trigger.orderbook();
        forceUpdateAccountOffers();
      });
    }
  };
}

export default Driver;
<|endoftext|>


<commit_before><commit_msg>cpuTemperatur() rewrite parsing hwmon (linux)<commit_after><|endoftext|>


<commit_before>var fs     = require('fs'),
    util   = require('util'),
    net    = require('net'),
    crypto = require('crypto');

var ssh2   = require('ssh2'),
    utils  = ssh2.utils,
    Class  = require('uclass');

var SshHost = new Class({

  initialize : function(server_rsa, new_device, validate_device, fetch_port, lost_device){
    var server = new ssh2.Server({ privateKey: server_rsa }, this.new_client.bind(this));
    this.new_device      = new_device || function() {};
    this.validate_device = validate_device;
    this.fetch_port      = fetch_port;
    this.lost_device     = lost_device;
    this.listen = server.listen.bind(server);
  },

  new_client : function(client) {
    var self = this;

    client.once('request', this.forward_request.bind(this, client));
    client.on('authentication', this.check_authentication.bind(this, client));

    client.on('error', function(err){
        console.log("Client on error", err);
    });

    client.once('end', function(){
      console.log("SSH lnk disconnected, local binding was %s", client.localPort);
      if(client.localNetServer)
        try {
          client.localNetServer.close();
        } catch(e) { } //throw an error if server is not listening
      if(client.device_key && client.localPort) {
        self.lost_device(client);
      }
    })

    this.new_device(client);
  },

  check_authentication : function(client, ctx) {
    client.username = ctx.username;

    if(!(ctx.method === 'publickey' && ctx.key.algo == "ssh-rsa"))
      return ctx.reject(['password', 'publickey'], true);

    var pem = utils.genPublicKey({public:ctx.key.data, type:'rsa'}).publicOrig;

    this.validate_device(ctx.key.data.toString('base64'), function(err, details) {
      if(err || !details.device_key)
        return ctx.reject(['password', 'publickey'], true);

      client.device_key = details.device_key;
      client.remote     = details;

      console.log("New client, validated device key is '%s'.", client.device_key, err );

      if (ctx.signature) {
        console.log("Verify signature");
        var verifier = crypto.createVerify(ctx.sigAlgo);
        verifier.update(ctx.blob);
        if (verifier.verify(pem, ctx.signature, 'binary'))
          ctx.accept();
        else
          ctx.reject(['password', 'publickey'], true);
      } else {
        // if no signature present, that means the client is just checking
        // the validity of the given public key
        ctx.accept();
      }
    });
  },

  forward_request : function(client, accept, reject, name, info){

    if(name != "tcpip-forward")
      return reject();

        //already listening
    if(client.localNetServer)
      return reject();

    var server = net.createServer(function(c){
      var out = client.forwardOut(
        info.bindAddr, info.bindPort,  //this is falsy, but we don't care
        c.remoteAddress, c.remotePort, function(err, channel){
          if(err) {
            //if the device is refusing lnks, maybe we should kill it ..
            console.log("Revert fowarding as been declined", err);
            return;
          }

          channel.pipe(c);
          c.pipe(channel);

          c.on("end", function(){
            console.log('Request is done');
          });
      });

      console.log("Request forward", out);
    });

    client.localNetServer = server;
    this.fetch_port(client, function(err, port) {
      console.log("Fetched remote port ", port);

      client.localPort = port;

      if(err)
        return reject();

      accept();

      server.listen(port, function() {
        console.log("Server forwarding lnk bound at %d ", port);
      });
      server.on('error', function(){
       client.end();
       //throw "Failed to listen to " + port;
      });

    });
  },

});

module.exports = SshHost;
<commit_msg>Proper drop of disconnected clients users<commit_after>var fs     = require('fs'),
    util   = require('util'),
    net    = require('net'),
    crypto = require('crypto');

var ssh2   = require('ssh2'),
    utils  = ssh2.utils,
    Class  = require('uclass');

var SshHost = new Class({

  initialize : function(server_rsa, new_device, validate_device, fetch_port, lost_device){
    var server = new ssh2.Server({ privateKey: server_rsa }, this.new_client.bind(this));
    this.new_device      = new_device || function() {};
    this.validate_device = validate_device;
    this.fetch_port      = fetch_port;
    this.lost_device     = lost_device;
    this.listen = server.listen.bind(server);
  },

  new_client : function(client) {
    var self = this;

    client.once('request', this.forward_request.bind(this, client));
    client.on('authentication', this.check_authentication.bind(this, client));

    client.on('error', function(err){
        console.log("Client on error", err);
    });

    client.once('end', function(){
      console.log("Client %s disconnected, local binding was %s", client.device_key, client.localPort);
      if(client.localNetServer)
        try {
          client.localNetServer.close();
        } catch(e) { } //throw an error if server is not listening
      if(client.device_key && client.localPort) {
        self.lost_device(client);
      }
    })

    this.new_device(client);
  },

  check_authentication : function(client, ctx) {
    client.username = ctx.username;

    if(!(ctx.method === 'publickey' && ctx.key.algo == "ssh-rsa"))
      return ctx.reject(['password', 'publickey'], true);

    var pem = utils.genPublicKey({public:ctx.key.data, type:'rsa'}).publicOrig;

    this.validate_device(ctx.key.data.toString('base64'), function(err, details) {
      if(err || !details.device_key)
        return ctx.reject(['password', 'publickey'], true);

      client.device_key = details.device_key;
      client.remote     = details;

      console.log("New client, validated device key is '%s'.", client.device_key, err );

      if (ctx.signature) {
        console.log("Verify signature");
        var verifier = crypto.createVerify(ctx.sigAlgo);
        verifier.update(ctx.blob);
        if (verifier.verify(pem, ctx.signature, 'binary'))
          ctx.accept();
        else
          ctx.reject(['password', 'publickey'], true);
      } else {
        // if no signature present, that means the client is just checking
        // the validity of the given public key
        ctx.accept();
      }
    });
  },

  forward_request : function(client, accept, reject, name, info){

    if(name != "tcpip-forward")
      return reject();

        //already listening
    if(client.localNetServer)
      return reject();

    var server = net.createServer(function(c){
      var out = client.forwardOut(
        info.bindAddr, info.bindPort,  //this is falsy, but we don't care
        c.remoteAddress, c.remotePort, function(err, channel){
          if(err) {
            //if the device is refusing lnks, maybe we should kill it ..
            console.log("Revert fowarding as been declined", err);
            return;
          }

          channel.pipe(c);
          c.pipe(channel);

          c.on("end", function(){
            console.log('Request is done');
          });

          client.once('end', function() {
            c.destroy();
          });
      });

      console.log("Request forward", out);
    });

    client.localNetServer = server;
    this.fetch_port(client, function(err, port) {
      console.log("Fetched remote port ", port);

      client.localPort = port;

      if(err)
        return reject();

      accept();

      server.listen(port, function() {
        console.log("Server forwarding lnk bound at %d ", port);
      });
      server.on('error', function(){
       client.end();
       //throw "Failed to listen to " + port;
      });

    });
  },

});

module.exports = SshHost;
<|endoftext|>


<commit_before>"use strict";

/* -------- General Functions -------- */

/**
 * Remove an element from an array
 * @param {array} array
 * @param {element} element
 */
function remove(array, element) {
  array.splice(array.indexOf(element), 1);
}

/**
 * Find element by id in an array
 * @param {Array} array
 * @param {id} id
 * @return {element|undefined}
 */
function find(array, id) {
  for (var i = 0; i < array.length; i++) {
    if (array[i].id === id) return array[i];
  }
  return undefined;
}

/**
 * Find element index by id
 * @param {Array} array
 * @param {id} id
 * @return {Number|undefined}
 */
function findIndex(array, id) {
  for (var i = 0; i < array.length; i++) {
    if (array[i].id === id) return i;
  }
  return undefined;
}

/**
 * Apply style to an elements
 * @param {DOMElement} element
 * @param {Object} style
 */
function applyStyleOn(element, style) {
  for (var key in style) {
    element.style[key] = style[key];
  }
};

/**
 * Apply attributes to an elements
 * @param {DOMElement} element
 * @param {Object} attributes
 */
function applyAttributesOn(element, attributes) {
  for (var key in attributes) {
    element.setAttribute(key, attributes[key]);
  }
};

function createUIElement(type, attributes, events) {
  var element = document.createElement(type);
  attributes = attributes || {};
  events = events || {};

  applyAttributesOn(element, attributes);

  for (var event in events) {
    element.addEventListener(event, events[event]);
  }

  return element;
}

/* -------- End General Functions -------- */<commit_msg>Define generic shared Game class<commit_after>"use strict";

/* -------- General Functions -------- */

/**
 * Remove an element from an array
 * @param {array} array
 * @param {element} element
 */
function remove(array, element) {
  array.splice(array.indexOf(element), 1);
}

/**
 * Find element by id in an array
 * @param {Array} array
 * @param {id} id
 * @return {element|undefined}
 */
function find(array, id) {
  for (var i = 0; i < array.length; i++) {
    if (array[i].id === id) return array[i];
  }
  return undefined;
}

/**
 * Find element index by id
 * @param {Array} array
 * @param {id} id
 * @return {Number|undefined}
 */
function findIndex(array, id) {
  for (var i = 0; i < array.length; i++) {
    if (array[i].id === id) return i;
  }
  return undefined;
}

/**
 * Apply style to an elements
 * @param {DOMElement} element
 * @param {Object} style
 */
function applyStyleOn(element, style) {
  for (var key in style) {
    element.style[key] = style[key];
  }
};

/**
 * Apply attributes to an elements
 * @param {DOMElement} element
 * @param {Object} attributes
 */
function applyAttributesOn(element, attributes) {
  for (var key in attributes) {
    element.setAttribute(key, attributes[key]);
  }
};

function createUIElement(type, attributes, events) {
  var element = document.createElement(type);
  attributes = attributes || {};
  events = events || {};

  applyAttributesOn(element, attributes);

  for (var event in events) {
    element.addEventListener(event, events[event]);
  }

  return element;
}

/* -------- End General Functions -------- */

/* -------- Game Class -------- */

/**
 * Game Class
 * @param {socket} socket
 */
function Game(socket, options) {
  this.room = socket;
  this.id = this.room.id;
  this.dungeons = [];
  this.started = false;
  this.time = 0;
  this.options = options || [
    'Wall',
    'Trap',
  ];
}

Game.prototype = {
  stop: function () {
    this.started = false;
  },
  start: function () {
    if (!this.started) {
      this.started = new Date().now;
    }
  },
  applyModifiers: function (key, dungeon) {
    return dungeon.config[key] + dungeon.modifiers[key];
  },
  removeDungeon: function (dungeonId) {
    var dungeonIndex = findIndex(this.dungeons, dungeonId);
    if (dungeonIndex) { this.dungeons.splice(index, 1); }
  },
  addDungeon: function (dungeon) {
    var refDungeon = find(this.dungeons, dungeon.id);
    if (!refDungeon) this.dungeons.push(dungeon);
    else {
      console.warn(dungeon.id + ' already exists in this game.');
    }
  },
  checkReady: function () {
    if (!this.started) {
      var isReady = false;
      for (var i = 0; i < this.dungeons.length; i++) {
        isReady = this.dungeons[i].player.ready;
        if (isReady === false) break;
      }
      if (isReady) this.start();
    }
  },
  updateGame: function (game) {
    this.options = game.options;
    this.updateDungeons(game.dungeons);
    this.checkReady();
  },
  /// @TODO review this function completly
  updateDungeons: function (dungeons) {
    var self = this;
    var maxIndex = Math.max(Math.max(self.dungeons.length - 1, dungeons.length - 1), 0);
    var dungeonsToAdd = [];

    function treatDungeons(index) {

      if (index < 0) { return; }

      var dungeon = dungeons[index];
      var refDungeon = dungeon ? find(self.dungeons, dungeon.id) : undefined;

      if (refDungeon && dungeon && refDungeon.id === dungeon.id) {
        refDungeon.area = dungeon.area;
        refDungeon.life = dungeon.life;
        refDungeon.money = dungeon.money;
        refDungeon.player = dungeon.player;
      } else if (!refDungeon && dungeon) {
        self.dungeons.push(dungeon);
      } else if (self.dungeons[index] && !dungeon && !find(dungeons, self.dungeons[index].id)) {
        var deletedDungeon = self.dungeons.splice(index, 1)[0];
        self.dungeonsUI.splice(index, 1);
      }

      treatDungeons(index--);
    }

    treatDungeons(maxIndex);
  },

  toJSON: function () {
    return {
      id: this.id,
      time: this.time,
      started: this.started,
      dungeons: this.dungeons.map(function (dungeon) {
        return dungeon.toJSON ? dungeon.toJSON() : dungeon;
      }),
      options: this.options,
    }
  }
}

/* -------- End Game Class -------- */
<|endoftext|>


<commit_before>/*
 * This file is part of the nivo project.
 *
 * Copyright 2016-present, Raphaël Benitte.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
import React from 'react'
import PropTypes from 'prop-types'
import compose from 'recompose/compose'
import withPropsOnChange from 'recompose/withPropsOnChange'
import pure from 'recompose/pure'
import { BasicTooltip } from '@nivo/core'

const BarItem = ({
    data,

    x,
    y,
    width,
    height,
    borderRadius,
    color,
    borderWidth,
    borderColor,

    label,
    shouldRenderLabel,
    labelColor,

    showTooltip,
    hideTooltip,
    onClick,
    onMouseEnter,
    onMouseLeave,
    tooltip,

    theme,
}) => {
    const handleTooltip = e => showTooltip(tooltip, e)
    const handleMouseEnter = e => {
        onMouseEnter(data, e)
        showTooltip(tooltip, e)
    }
    const handleMouseLeave = e => {
        onMouseLeave(data, e)
        hideTooltip(e)
    }

    return (
        <g transform={`translate(${x}, ${y})`}>
            <rect
                width={width}
                height={height}
                rx={borderRadius}
                ry={borderRadius}
                fill={data.fill ? data.fill : color}
                strokeWidth={borderWidth}
                stroke={borderColor}
                onMouseEnter={handleMouseEnter}
                onMouseMove={handleTooltip}
                onMouseLeave={handleMouseLeave}
                onClick={onClick}
            />
            {shouldRenderLabel && (
                <text
                    x={width / 2}
                    y={height / 2}
                    textAnchor="middle"
                    alignmentBaseline="central"
                    style={{
                        ...theme.labels.text,
                        pointerEvents: 'none',
                        fill: labelColor,
                    }}
                >
                    {label}
                </text>
            )}
        </g>
    )
}

BarItem.propTypes = {
    data: PropTypes.shape({
        id: PropTypes.string.isRequired,
        value: PropTypes.number.isRequired,
        indexValue: PropTypes.string.isRequired,
    }).isRequired,

    x: PropTypes.number.isRequired,
    y: PropTypes.number.isRequired,
    width: PropTypes.number.isRequired,
    height: PropTypes.number.isRequired,
    color: PropTypes.string.isRequired,
    borderRadius: PropTypes.number.isRequired,
    borderWidth: PropTypes.number.isRequired,
    borderColor: PropTypes.string.isRequired,

    label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    shouldRenderLabel: PropTypes.bool.isRequired,
    labelColor: PropTypes.string.isRequired,

    showTooltip: PropTypes.func.isRequired,
    hideTooltip: PropTypes.func.isRequired,
    getTooltipLabel: PropTypes.func.isRequired,
    tooltipFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    onClick: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    tooltip: PropTypes.element.isRequired,

    theme: PropTypes.shape({
        tooltip: PropTypes.shape({}).isRequired,
    }).isRequired,
}

const enhance = compose(
    withPropsOnChange(['data', 'color', 'onClick'], ({ data, color, onClick }) => ({
        onClick: event => onClick({ color, ...data }, event),
    })),
    withPropsOnChange(
        ['data', 'color', 'theme', 'tooltip', 'getTooltipLabel', 'tooltipFormat'],
        ({ data, color, theme, tooltip, getTooltipLabel, tooltipFormat }) => ({
            tooltip: (
                <BasicTooltip
                    id={getTooltipLabel(data)}
                    value={data.value}
                    enableChip={true}
                    color={color}
                    theme={theme}
                    format={tooltipFormat}
                    renderContent={
                        typeof tooltip === 'function'
                            ? tooltip.bind(null, { color, theme, ...data })
                            : null
                    }
                />
            ),
        })
    ),
    pure
)

export default enhance(BarItem)
<commit_msg>Fixed BarItem's tooltipFormat PropTypes<commit_after>/*
 * This file is part of the nivo project.
 *
 * Copyright 2016-present, Raphaël Benitte.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
import React from 'react'
import PropTypes from 'prop-types'
import compose from 'recompose/compose'
import withPropsOnChange from 'recompose/withPropsOnChange'
import pure from 'recompose/pure'
import { BasicTooltip } from '@nivo/core'

const BarItem = ({
    data,

    x,
    y,
    width,
    height,
    borderRadius,
    color,
    borderWidth,
    borderColor,

    label,
    shouldRenderLabel,
    labelColor,

    showTooltip,
    hideTooltip,
    onClick,
    onMouseEnter,
    onMouseLeave,
    tooltip,

    theme,
}) => {
    const handleTooltip = e => showTooltip(tooltip, e)
    const handleMouseEnter = e => {
        onMouseEnter(data, e)
        showTooltip(tooltip, e)
    }
    const handleMouseLeave = e => {
        onMouseLeave(data, e)
        hideTooltip(e)
    }

    return (
        <g transform={`translate(${x}, ${y})`}>
            <rect
                width={width}
                height={height}
                rx={borderRadius}
                ry={borderRadius}
                fill={data.fill ? data.fill : color}
                strokeWidth={borderWidth}
                stroke={borderColor}
                onMouseEnter={handleMouseEnter}
                onMouseMove={handleTooltip}
                onMouseLeave={handleMouseLeave}
                onClick={onClick}
            />
            {shouldRenderLabel && (
                <text
                    x={width / 2}
                    y={height / 2}
                    textAnchor="middle"
                    alignmentBaseline="central"
                    style={{
                        ...theme.labels.text,
                        pointerEvents: 'none',
                        fill: labelColor,
                    }}
                >
                    {label}
                </text>
            )}
        </g>
    )
}

BarItem.propTypes = {
    data: PropTypes.shape({
        id: PropTypes.string.isRequired,
        value: PropTypes.number.isRequired,
        indexValue: PropTypes.string.isRequired,
    }).isRequired,

    x: PropTypes.number.isRequired,
    y: PropTypes.number.isRequired,
    width: PropTypes.number.isRequired,
    height: PropTypes.number.isRequired,
    color: PropTypes.string.isRequired,
    borderRadius: PropTypes.number.isRequired,
    borderWidth: PropTypes.number.isRequired,
    borderColor: PropTypes.string.isRequired,

    label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    shouldRenderLabel: PropTypes.bool.isRequired,
    labelColor: PropTypes.string.isRequired,

    showTooltip: PropTypes.func.isRequired,
    hideTooltip: PropTypes.func.isRequired,
    getTooltipLabel: PropTypes.func.isRequired,
    tooltipFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
    onClick: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    tooltip: PropTypes.element.isRequired,

    theme: PropTypes.shape({
        tooltip: PropTypes.shape({}).isRequired,
    }).isRequired,
}

const enhance = compose(
    withPropsOnChange(['data', 'color', 'onClick'], ({ data, color, onClick }) => ({
        onClick: event => onClick({ color, ...data }, event),
    })),
    withPropsOnChange(
        ['data', 'color', 'theme', 'tooltip', 'getTooltipLabel', 'tooltipFormat'],
        ({ data, color, theme, tooltip, getTooltipLabel, tooltipFormat }) => ({
            tooltip: (
                <BasicTooltip
                    id={getTooltipLabel(data)}
                    value={data.value}
                    enableChip={true}
                    color={color}
                    theme={theme}
                    format={tooltipFormat}
                    renderContent={
                        typeof tooltip === 'function'
                            ? tooltip.bind(null, { color, theme, ...data })
                            : null
                    }
                />
            ),
        })
    ),
    pure
)

export default enhance(BarItem)
<|endoftext|>


<commit_before>const pluralize = require('pluralize');
const { escapeRegExp } = require('@keystonejs/utils');

const upcase = str => str.substr(0, 1).toUpperCase() + str.substr(1);

const keyToLabel = str =>
  str
    .replace(/([a-z])([A-Z])/g, '$1 $2')
    .split(/\s|_|\-/)
    .filter(i => i)
    .map(upcase)
    .join(' ');

const labelToPath = str =>
  str
    .split(' ')
    .join('-')
    .toLowerCase();

const labelToClass = str => str.replace(/\s+/g, '');

module.exports = class List {
  constructor(key, config, { getListByKey, mongoose }) {
    this.key = key;

    this.config = {
      labelResolver: item => item[config.labelField || 'name'] || item.id,
      ...config,
    };
    this.getListByKey = getListByKey;

    const label = keyToLabel(key);
    const singular = pluralize.singular(label);
    const plural = pluralize.plural(label);

    this.label = config.label || plural;
    this.singular = config.singular || singular;
    this.plural = config.plural || plural;
    this.path = config.path || labelToPath(plural);

    const itemQueryName = config.itemQueryName || labelToClass(singular);
    const listQueryName = config.listQueryName || labelToClass(plural);

    this.itemQueryName = itemQueryName;
    this.listQueryName = `all${listQueryName}`;
    this.listQueryMetaName = `_${this.listQueryName}Meta`;
    this.deleteMutationName = `delete${itemQueryName}`;
    this.deleteManyMutationName = `delete${listQueryName}`;
    this.updateMutationName = `update${itemQueryName}`;
    this.createMutationName = `create${itemQueryName}`;

    this.fields = config.fields
      ? Object.keys(config.fields).map(path => {
          const { type, ...fieldSpec } = config.fields[path];
          const implementation = type.implementation;
          return new implementation(path, fieldSpec, {
            getListByKey,
            listKey: key,
          });
        })
      : [];

    this.views = {};
    Object.entries(config.fields).forEach(([path, fieldConfig]) => {
      const fieldType = fieldConfig.type;
      this.views[path] = {};

      Object.entries(fieldType.views).forEach(
        ([fieldViewType, fieldViewPath]) => {
          this.views[path][fieldViewType] = fieldViewPath;
        }
      );
    });

    const schema = new mongoose.Schema({}, this.config.mongooseSchemaOptions);
    this.fields.forEach(i => i.addToMongooseSchema(schema));

    if (this.config.configureMongooseSchema) {
      this.config.configureMongooseSchema(schema, { mongoose });
    }

    this.model = mongoose.model(this.key, schema);
  }
  getAdminMeta() {
    return {
      key: this.key,
      label: this.label,
      singular: this.singular,
      plural: this.plural,
      path: this.path,
      listQueryName: this.listQueryName,
      listQueryMetaName: this.listQueryMetaName,
      itemQueryName: this.itemQueryName,
      createMutationName: this.createMutationName,
      updateMutationName: this.updateMutationName,
      deleteMutationName: this.deleteMutationName,
      deleteManyMutationName: this.deleteManyMutationName,
      fields: this.fields.map(i => i.getAdminMeta()),
      views: this.views,
    };
  }
  getAdminGraphqlTypes() {
    const fieldSchemas = this.fields
      .map(i => i.getGraphqlSchema())
      .join('\n        ');
    const fieldTypes = this.fields
      .map(i => i.getGraphqlTypes())
      .filter(i => i)
      .join('\n      ');
    const updateArgs = this.fields
      .map(i => i.getGraphqlUpdateArgs())
      .filter(i => i)
      .map(i => i.split(/\n\s+/g).join('\n        '))
      .join('\n        ')
      .trim();
    const createArgs = this.fields
      .map(i => i.getGraphqlCreateArgs())
      .filter(i => i)
      .map(i => i.split(/\n\s+/g).join('\n        '))
      .join('\n        ')
      .trim();
    return `
      type ${this.key} {
        id: String
        # This virtual field will be resolved in one of the following ways (in this order):
        # 1. Execution of 'labelResolver' set on the ${this.key} List config, or
        # 2. As an alias to the field set on 'labelField' in the ${
          this.key
        } List config, or
        # 3. As an alias to a 'name' field on the ${
          this.key
        } List (if one exists), or
        # 4. As an alias to the 'id' field on the ${this.key} List.
        _label_: String
        ${fieldSchemas}
      }
      input ${this.key}UpdateInput {
        ${updateArgs}
      }
      input ${this.key}CreateInput {
        ${createArgs}
      }
      ${fieldTypes}`;
  }
  getAdminGraphqlQueries() {
    const queryArgs = this.fields
      .map(i => i.getGraphqlQueryArgs())
      .filter(i => i)
      .map(i => i.split(/\n\s+/g).join('\n          '))
      .join('\n          # New field\n          ');
    const commonArgs = `
          search: String
          sort: String

          # Pagination
          first: Int
          skip: Int`;
    // TODO: Group field filters under filter: FilterInput
    return `
        ${this.listQueryName}(${commonArgs}

          # Field Filters
          ${queryArgs}
        ): [${this.key}]

        ${this.itemQueryName}(id: String!): ${this.key}

        ${this.listQueryMetaName}(${commonArgs}

          # Field Filters
          ${queryArgs}
        ): _QueryMeta`;
  }
  getAdminQueryResolvers() {
    return {
      [this.listQueryName]: (_, args) => this.buildItemsQuery(args),
      [this.listQueryMetaName]: (_, args) => this.buildItemsQueryMeta(args),
      [this.itemQueryName]: (_, { id }) => this.model.findById(id),
    };
  }
  getAdminFieldResolvers() {
    const fieldResolvers = this.fields.reduce(
      (resolvers, field) => ({ ...resolvers, ...field.getGraphqlResolvers() }),
      {
        _label_: this.config.labelResolver,
      }
    );
    return { [this.key]: fieldResolvers };
  }
  getAdminGraphqlMutations() {
    return `
        ${this.createMutationName}(
          data: ${this.key}UpdateInput
        ): ${this.key}
        ${this.updateMutationName}(
          id: String!
          data: ${this.key}UpdateInput
        ): ${this.key}
        ${this.deleteMutationName}(
          id: String!
        ): ${this.key}
        ${this.deleteManyMutationName}(
          ids: [String!]
        ): ${this.key}`;
  }
  getAdminMutationResolvers() {
    return {
      [this.createMutationName]: async (_, { data }) => {
        return this.model.create(data);
      },
      [this.updateMutationName]: async (_, { id, data }) => {
        const item = await this.model.findById(id);
        // TODO: Loop through each field and have it apply the update
        item.set(data);
        return item.save();
      },
      [this.deleteMutationName]: (_, { id }) =>
        this.model.findByIdAndRemove(id),
      [this.deleteManyMutationName]: async (_, { ids }) => {
        ids.map(async id => await this.model.findByIdAndRemove(id));
      },
    };
  }
  buildItemsQuery(args) {
    let conditions = this.fields.reduce((conds, field) => {
      const fieldConditions = field.getQueryConditions(args);
      if (!fieldConditions.length) return conds;
      return [...conds, ...fieldConditions.map(i => ({ [field.path]: i }))];
    }, []);
    if (args.search) {
      // TODO: Implement configurable search fields for lists
      conditions.push({
        name: new RegExp(`^${escapeRegExp(args.search)}`, 'i'),
      });
    }
    if (!conditions.length) conditions = undefined;
    else if (conditions.length === 1) conditions = conditions[0];
    else conditions = { $and: conditions };

    const query = this.model.find(conditions);

    if (args.sort) {
      query.sort(args.sort);
    }

    if (args.first) {
      query.limit(args.first);
    }
    if (args.skip) {
      query.skip(args.skip);
    }
    return query;
  }
  buildItemsQueryMeta(args) {
    return new Promise((resolve, reject) => {
      this.buildItemsQuery(args).count((err, count) => {
        if (err) reject(err);
        resolve({ count });
      });
    });
  }
};
<commit_msg>Disallow ambiguously plural list names (eg; Fish or Equipment)<commit_after>const pluralize = require('pluralize');
const { escapeRegExp } = require('@keystonejs/utils');

const upcase = str => str.substr(0, 1).toUpperCase() + str.substr(1);

const keyToLabel = str =>
  str
    .replace(/([a-z])([A-Z])/g, '$1 $2')
    .split(/\s|_|\-/)
    .filter(i => i)
    .map(upcase)
    .join(' ');

const labelToPath = str =>
  str
    .split(' ')
    .join('-')
    .toLowerCase();

const labelToClass = str => str.replace(/\s+/g, '');

module.exports = class List {
  constructor(key, config, { getListByKey, mongoose }) {
    this.key = key;

    this.config = {
      labelResolver: item => item[config.labelField || 'name'] || item.id,
      ...config,
    };
    this.getListByKey = getListByKey;

    const label = keyToLabel(key);
    const singular = pluralize.singular(label);
    const plural = pluralize.plural(label);

    if (plural === label) {
      throw new Error(
        `Unable to use ${label} as a List name - it has an ambiguous plural (${plural}). Please choose another name for your list.`
      );
    }

    this.label = config.label || plural;
    this.singular = config.singular || singular;
    this.plural = config.plural || plural;
    this.path = config.path || labelToPath(plural);

    const itemQueryName = config.itemQueryName || labelToClass(singular);
    const listQueryName = config.listQueryName || labelToClass(plural);

    this.itemQueryName = itemQueryName;
    this.listQueryName = `all${listQueryName}`;
    this.listQueryMetaName = `_${this.listQueryName}Meta`;
    this.deleteMutationName = `delete${itemQueryName}`;
    this.deleteManyMutationName = `delete${listQueryName}`;
    this.updateMutationName = `update${itemQueryName}`;
    this.createMutationName = `create${itemQueryName}`;

    this.fields = config.fields
      ? Object.keys(config.fields).map(path => {
          const { type, ...fieldSpec } = config.fields[path];
          const implementation = type.implementation;
          return new implementation(path, fieldSpec, {
            getListByKey,
            listKey: key,
          });
        })
      : [];

    this.views = {};
    Object.entries(config.fields).forEach(([path, fieldConfig]) => {
      const fieldType = fieldConfig.type;
      this.views[path] = {};

      Object.entries(fieldType.views).forEach(
        ([fieldViewType, fieldViewPath]) => {
          this.views[path][fieldViewType] = fieldViewPath;
        }
      );
    });

    const schema = new mongoose.Schema({}, this.config.mongooseSchemaOptions);
    this.fields.forEach(i => i.addToMongooseSchema(schema));

    if (this.config.configureMongooseSchema) {
      this.config.configureMongooseSchema(schema, { mongoose });
    }

    this.model = mongoose.model(this.key, schema);
  }
  getAdminMeta() {
    return {
      key: this.key,
      label: this.label,
      singular: this.singular,
      plural: this.plural,
      path: this.path,
      listQueryName: this.listQueryName,
      listQueryMetaName: this.listQueryMetaName,
      itemQueryName: this.itemQueryName,
      createMutationName: this.createMutationName,
      updateMutationName: this.updateMutationName,
      deleteMutationName: this.deleteMutationName,
      deleteManyMutationName: this.deleteManyMutationName,
      fields: this.fields.map(i => i.getAdminMeta()),
      views: this.views,
    };
  }
  getAdminGraphqlTypes() {
    const fieldSchemas = this.fields
      .map(i => i.getGraphqlSchema())
      .join('\n        ');
    const fieldTypes = this.fields
      .map(i => i.getGraphqlTypes())
      .filter(i => i)
      .join('\n      ');
    const updateArgs = this.fields
      .map(i => i.getGraphqlUpdateArgs())
      .filter(i => i)
      .map(i => i.split(/\n\s+/g).join('\n        '))
      .join('\n        ')
      .trim();
    const createArgs = this.fields
      .map(i => i.getGraphqlCreateArgs())
      .filter(i => i)
      .map(i => i.split(/\n\s+/g).join('\n        '))
      .join('\n        ')
      .trim();
    return `
      type ${this.key} {
        id: String
        # This virtual field will be resolved in one of the following ways (in this order):
        # 1. Execution of 'labelResolver' set on the ${this.key} List config, or
        # 2. As an alias to the field set on 'labelField' in the ${
          this.key
        } List config, or
        # 3. As an alias to a 'name' field on the ${
          this.key
        } List (if one exists), or
        # 4. As an alias to the 'id' field on the ${this.key} List.
        _label_: String
        ${fieldSchemas}
      }
      input ${this.key}UpdateInput {
        ${updateArgs}
      }
      input ${this.key}CreateInput {
        ${createArgs}
      }
      ${fieldTypes}`;
  }
  getAdminGraphqlQueries() {
    const queryArgs = this.fields
      .map(i => i.getGraphqlQueryArgs())
      .filter(i => i)
      .map(i => i.split(/\n\s+/g).join('\n          '))
      .join('\n          # New field\n          ');
    const commonArgs = `
          search: String
          sort: String

          # Pagination
          first: Int
          skip: Int`;
    // TODO: Group field filters under filter: FilterInput
    return `
        ${this.listQueryName}(${commonArgs}

          # Field Filters
          ${queryArgs}
        ): [${this.key}]

        ${this.itemQueryName}(id: String!): ${this.key}

        ${this.listQueryMetaName}(${commonArgs}

          # Field Filters
          ${queryArgs}
        ): _QueryMeta`;
  }
  getAdminQueryResolvers() {
    return {
      [this.listQueryName]: (_, args) => this.buildItemsQuery(args),
      [this.listQueryMetaName]: (_, args) => this.buildItemsQueryMeta(args),
      [this.itemQueryName]: (_, { id }) => this.model.findById(id),
    };
  }
  getAdminFieldResolvers() {
    const fieldResolvers = this.fields.reduce(
      (resolvers, field) => ({ ...resolvers, ...field.getGraphqlResolvers() }),
      {
        _label_: this.config.labelResolver,
      }
    );
    return { [this.key]: fieldResolvers };
  }
  getAdminGraphqlMutations() {
    return `
        ${this.createMutationName}(
          data: ${this.key}UpdateInput
        ): ${this.key}
        ${this.updateMutationName}(
          id: String!
          data: ${this.key}UpdateInput
        ): ${this.key}
        ${this.deleteMutationName}(
          id: String!
        ): ${this.key}
        ${this.deleteManyMutationName}(
          ids: [String!]
        ): ${this.key}`;
  }
  getAdminMutationResolvers() {
    return {
      [this.createMutationName]: async (_, { data }) => {
        return this.model.create(data);
      },
      [this.updateMutationName]: async (_, { id, data }) => {
        const item = await this.model.findById(id);
        // TODO: Loop through each field and have it apply the update
        item.set(data);
        return item.save();
      },
      [this.deleteMutationName]: (_, { id }) =>
        this.model.findByIdAndRemove(id),
      [this.deleteManyMutationName]: async (_, { ids }) => {
        ids.map(async id => await this.model.findByIdAndRemove(id));
      },
    };
  }
  buildItemsQuery(args) {
    let conditions = this.fields.reduce((conds, field) => {
      const fieldConditions = field.getQueryConditions(args);
      if (!fieldConditions.length) return conds;
      return [...conds, ...fieldConditions.map(i => ({ [field.path]: i }))];
    }, []);
    if (args.search) {
      // TODO: Implement configurable search fields for lists
      conditions.push({
        name: new RegExp(`^${escapeRegExp(args.search)}`, 'i'),
      });
    }
    if (!conditions.length) conditions = undefined;
    else if (conditions.length === 1) conditions = conditions[0];
    else conditions = { $and: conditions };

    const query = this.model.find(conditions);

    if (args.sort) {
      query.sort(args.sort);
    }

    if (args.first) {
      query.limit(args.first);
    }
    if (args.skip) {
      query.skip(args.skip);
    }
    return query;
  }
  buildItemsQueryMeta(args) {
    return new Promise((resolve, reject) => {
      this.buildItemsQuery(args).count((err, count) => {
        if (err) reject(err);
        resolve({ count });
      });
    });
  }
};
<|endoftext|>


<commit_before>DocsController.$inject = ['$location', '$browser', '$window', '$cookies'];
function DocsController($location, $browser, $window, $cookies) {
  window.$root = this.$root;

  var scope = this,
      OFFLINE_COOKIE_NAME = 'ng-offline',
      DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)/,
      INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;

  scope.$location = $location;
  scope.versionNumber = angular.version.full;
  scope.version = angular.version.full + "  " + angular.version.codeName;
  scope.subpage = false;
  scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);

  if (!$location.path() || INDEX_PATH.test($location.path())) {
    $location.path('/api').replace();
  }

  scope.$watch('$location.path()', function(scope, path) {
    // ignore non-doc links which are used in examples
    if (DOCS_PATH.test(path)) {
      var parts = path.split('/');
      scope.sectionId = parts[1];
      scope.partialId = parts[2] || 'index';
      scope.pages = angular.Array.filter(NG_PAGES, {section: scope.sectionId});

      var i = scope.pages.length;
      while (i--) {
        if (scope.pages[i].id == scope.partialId) {
          scope.partialTitle = scope.pages[i].name;
          break;
        }
      }
      if (i<0) {
        scope.partialTitle = 'Error: Page Not Found!';
        delete scope.partialId;
      }
    }
  });

  scope.getUrl = function(page) {
    return page.section + '/' + page.id;
  };

  scope.getCurrentPartial = function() {
    return this.partialId ? ('./partials/' + this.sectionId + '/' + this.partialId + '.html') : '';
  };

  scope.getClass = function(page) {
    var depth = page.depth,
        cssClass = 'level-' + depth + (page.name == this.partialId ? ' selected' : '');

    if (page.section == 'api')
      cssClass += ' monospace';

    return cssClass;
  };

  scope.selectedSection = function(section) {
    return section == scope.sectionId ? 'current' : '';
  };

  scope.selectedPartial = function(partial) {
    return partial.id == scope.partialId ? 'current' : '';
  };

  scope.afterPartialLoaded = function() {
    SyntaxHighlighter.highlight();
    $window.scrollTo(0,0);
    $window._gaq.push(['_trackPageview', $location.path()]);
  };

  scope.getFeedbackUrl = function() {
    return "mailto:angular@googlegroups.com?" +
           "subject=" + escape("Feedback on " + $location.absUrl()) + "&" +
           "body=" + escape("Hi there,\n\nI read " + $location.absUrl() + " and wanted to ask ....");
  };

  /** stores a cookie that is used by apache to decide which manifest ot send */
  scope.enableOffline = function() {
    //The cookie will be good for one year!
    var date = new Date();
    date.setTime(date.getTime()+(365*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
    var value = angular.version.full;
    document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;

    //force the page to reload so server can serve new manifest file
    window.location.reload(true);
  };

  // bind escape to hash reset callback
  angular.element(window).bind('keydown', function(e) {
    if (e.keyCode === 27) {
      scope.subpage = false;
      scope.$eval();
    }
  });
}

// prevent compilation of code
angular.widget('code', function(element) {
  element.attr('ng:non-bindable', 'true');
});

SyntaxHighlighter['defaults'].toolbar = false;
SyntaxHighlighter['defaults'].gutter = true;

/**
 * Controller for tutorial instructions
 * @param $cookieStore
 * @constructor
 */
function TutorialInstructionsCtrl($cookieStore) {
  this.selected = $cookieStore.get('selEnv') || 'git-mac';

  this.currentCls = function(id, cls) {
    return this.selected == id  ? cls || 'current' : '';
  };

  this.select = function(id) {
    this.selected = id;
    $cookieStore.put('selEnv', id);
  };
}

angular.service('$locationConfig', function() {
  return {
    html5Mode: true,
    hashPrefix: '!'
  };
});
<commit_msg>fix(docs): update page title only when content loads<commit_after>DocsController.$inject = ['$location', '$browser', '$window', '$cookies'];
function DocsController($location, $browser, $window, $cookies) {
  window.$root = this.$root;

  var scope = this,
      OFFLINE_COOKIE_NAME = 'ng-offline',
      DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)/,
      INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;

  scope.$location = $location;
  scope.versionNumber = angular.version.full;
  scope.version = angular.version.full + "  " + angular.version.codeName;
  scope.subpage = false;
  scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
  scope.futurePartialTitle = null;

  if (!$location.path() || INDEX_PATH.test($location.path())) {
    $location.path('/api').replace();
  }

  scope.$watch('$location.path()', function(scope, path) {
    // ignore non-doc links which are used in examples
    if (DOCS_PATH.test(path)) {
      var parts = path.split('/');
      scope.sectionId = parts[1];
      scope.partialId = parts[2] || 'index';
      scope.pages = angular.Array.filter(NG_PAGES, {section: scope.sectionId});

      var i = scope.pages.length;
      while (i--) {
        if (scope.pages[i].id == scope.partialId) {
          // TODO(i): this is not ideal but better than updating the title before a partial arrives,
          //   which results in the old partial being displayed with the new title
          scope.futurePartialTitle = scope.pages[i].name;
          break;
        }
      }
      if (i<0) {
        scope.partialTitle = 'Error: Page Not Found!';
        delete scope.partialId;
      }
    }
  });

  scope.getUrl = function(page) {
    return page.section + '/' + page.id;
  };

  scope.getCurrentPartial = function() {
    return this.partialId ? ('./partials/' + this.sectionId + '/' + this.partialId + '.html') : '';
  };

  scope.getClass = function(page) {
    var depth = page.depth,
        cssClass = 'level-' + depth + (page.name == this.partialId ? ' selected' : '');

    if (page.section == 'api')
      cssClass += ' monospace';

    return cssClass;
  };

  scope.selectedSection = function(section) {
    return section == scope.sectionId ? 'current' : '';
  };

  scope.selectedPartial = function(partial) {
    return partial.id == scope.partialId ? 'current' : '';
  };

  scope.afterPartialLoaded = function() {
    scope.partialTitle = scope.futurePartialTitle;
    SyntaxHighlighter.highlight();
    $window.scrollTo(0,0);
    $window._gaq.push(['_trackPageview', $location.path()]);
  };

  scope.getFeedbackUrl = function() {
    return "mailto:angular@googlegroups.com?" +
           "subject=" + escape("Feedback on " + $location.absUrl()) + "&" +
           "body=" + escape("Hi there,\n\nI read " + $location.absUrl() + " and wanted to ask ....");
  };

  /** stores a cookie that is used by apache to decide which manifest ot send */
  scope.enableOffline = function() {
    //The cookie will be good for one year!
    var date = new Date();
    date.setTime(date.getTime()+(365*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
    var value = angular.version.full;
    document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;

    //force the page to reload so server can serve new manifest file
    window.location.reload(true);
  };

  // bind escape to hash reset callback
  angular.element(window).bind('keydown', function(e) {
    if (e.keyCode === 27) {
      scope.subpage = false;
      scope.$eval();
    }
  });
}

// prevent compilation of code
angular.widget('code', function(element) {
  element.attr('ng:non-bindable', 'true');
});

SyntaxHighlighter['defaults'].toolbar = false;
SyntaxHighlighter['defaults'].gutter = true;

/**
 * Controller for tutorial instructions
 * @param $cookieStore
 * @constructor
 */
function TutorialInstructionsCtrl($cookieStore) {
  this.selected = $cookieStore.get('selEnv') || 'git-mac';

  this.currentCls = function(id, cls) {
    return this.selected == id  ? cls || 'current' : '';
  };

  this.select = function(id) {
    this.selected = id;
    $cookieStore.put('selEnv', id);
  };
}

angular.service('$locationConfig', function() {
  return {
    html5Mode: true,
    hashPrefix: '!'
  };
});
<|endoftext|>


<commit_before>/** @jsx React.DOM */

'use strict';

var React = require('react');

// sub-components
var TodoItem = require('./todo-item');

module.exports = React.createClass({
    displayName: 'TodoItem',

    handleComplete: function (e) {
        e.preventDefault();
        this.props.handleComplete(this.props);
    },

    render: function () {
        var itemStyle = {
            textDecoration: 'line-through'
        };

        return (

                    {this.props.text}

        );
    }
});
<commit_msg>remove circle ref<commit_after>/** @jsx React.DOM */

'use strict';

var React = require('react');

module.exports = React.createClass({
    displayName: 'TodoItem',

    handleComplete: function (e) {
        e.preventDefault();
        this.props.handleComplete(this.props);
    },

    render: function () {
        var itemStyle = {
            textDecoration: 'line-through'
        };

        return (

                    {this.props.text}

        );
    }
});
<|endoftext|>


<commit_before>'use strict';

var db = require('mano').db,
	user = db.User.prototype;

exports['step-form'] = { class: { 'step-active': true } };

exports.step = function () {
	h1("2. Fill the form");
	div({ class: 'error-main' },
		p(span({ class: 'fa fa-exclamation-circle' }), "Please fill the Guide first"));
	div(
		{ class: 'disabler-range', id: 'forms-disabler-range' },
		section(
			form(
				div({ class: 'section-primary' },
					h2("Business Owner basic informations"),
					hr(),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['firstName', 'lastName', 'dateOfBirth', 'userEmail'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						),
					p({ class: 'button-scroll-top' },
						a({ onclick: 'window.scroll(0, 0)' }, span({ class: 'fa fa-arrow-up' }, "Back to top"))
						)
					)
			)
		),

		section(
			{ class: 'section-primary' },
			form(
				h2("Business Owner secondary informations"),
				hr(),
				div({ class: 'sub-section' },
					h3("First subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					),
				div({ class: 'sub-section' },
					h3("Second subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						),
					p({ class: 'button-scroll-top' },
						a({ onclick: 'window.scroll(0, 0)' }, span({ class: 'fa fa-arrow-up' }, "Back to top"))
						)
					)
			)
		),

		section({ class: 'section-primary' },
			div(
				div(
					h2("Directors & non-directors owner / partners"),
					hr(),
					table(
						{ class: 'partners-list' },
						thead(
							tr(
								th({ class: 'desktop-only' }, "Entity"),
								th("First name"),
								th("Surname"),
								th({ class: 'desktop-only' }, "Director?"),
								th({ class: 'desktop-only' }, "Subscriber?"),
								th({ class: 'desktop-only' }, ""),
								th({ class: 'actions' }, "Actions")
							)
						),
						tbody(
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							)
						)
					),
					a(
						{ class: 'new-entity', href: '/forms/partner-add/' },
						"Add new partner"
					)
				)
			)
			),

		div({ class: 'next-step' },
			a({ href: '/documents/' }, "Continue to next step")
			),
		div({ class: 'disabler' })
	);
};
<commit_msg>Added gotop button under partners table. #162<commit_after>'use strict';

var db = require('mano').db,
	user = db.User.prototype;

exports['step-form'] = { class: { 'step-active': true } };

exports.step = function () {
	h1("2. Fill the form");
	div({ class: 'error-main' },
		p(span({ class: 'fa fa-exclamation-circle' }), "Please fill the Guide first"));
	div(
		{ class: 'disabler-range', id: 'forms-disabler-range' },
		section(
			form(
				div({ class: 'section-primary' },
					h2("Business Owner basic informations"),
					hr(),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['firstName', 'lastName', 'dateOfBirth', 'userEmail'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						),
					p({ class: 'button-scroll-top' },
						a({ onclick: 'window.scroll(0, 0)' }, span({ class: 'fa fa-arrow-up' }, "Back to top"))
						)
					)
			)
		),

		section(
			{ class: 'section-primary' },
			form(
				h2("Business Owner secondary informations"),
				hr(),
				div({ class: 'sub-section' },
					h3("First subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					),
				div({ class: 'sub-section' },
					h3("Second subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						),
					p({ class: 'button-scroll-top' },
						a({ onclick: 'window.scroll(0, 0)' }, span({ class: 'fa fa-arrow-up' }, "Back to top"))
						)
					)
			)
		),

		section({ class: 'section-primary' },
			div(
				div(
					h2("Directors & non-directors owner / partners"),
					hr(),
					table(
						{ class: 'partners-list' },
						thead(
							tr(
								th({ class: 'desktop-only' }, "Entity"),
								th("First name"),
								th("Surname"),
								th({ class: 'desktop-only' }, "Director?"),
								th({ class: 'desktop-only' }, "Subscriber?"),
								th({ class: 'desktop-only' }, ""),
								th({ class: 'actions' }, "Actions")
							)
						),
						tbody(
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							)
						)
					),
					a(
						{ class: 'new-entity', href: '/forms/partner-add/' },
						"Add new partner"
					)
				)
			),
			p({ class: 'button-scroll-top' },
				a({ onclick: 'window.scroll(0, 0)' }, span({ class: 'fa fa-arrow-up' }, "Back to top"))
				)
			),

		div({ class: 'next-step' },
			a({ href: '/documents/' }, "Continue to next step")
			),
		div({ class: 'disabler' })
	);
};
<|endoftext|>