<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import async from 'async';
import { AddressBlock } from 'components';
import FontAwesome from 'react-fontawesome';
import * as blockchainActions from '../../redux/modules/blockchain';
import * as metaCoinActions from '../../redux/modules/metaCoin';
import web3 from '../../web3';
import contractAddress from '../../../contractAddresses';

let MetaCoinContract;

@connect(state => (
  {
    // user: state.auth.user,
    coinbase: state.blockchain.coinbase,
    amountToSend: state.metaCoin.amountToSend,
    addressToSend: state.metaCoin.addressToSend,
    metaBalance: state.metaCoin.metaBalance,
    pastTransactions: state.metaCoin.pastTransactions,
    userBalances: state.metaCoin.userBalances
  }),
{
  ...blockchainActions,
  ...metaCoinActions
}
)
export default class MetaCoin extends Component {
  static propTypes = {
    // user: PropTypes.shape({
    //   email: PropTypes.string
    // }),
    coinbase: PropTypes.string.isRequired,
    setCoinbase: PropTypes.func.isRequired,
    loadContractABI: PropTypes.func.isRequired,
    setAmountToSend: PropTypes.func.isRequired,
    setAddressToSend: PropTypes.func.isRequired,
    amountToSend: PropTypes.string.isRequired,
    addressToSend: PropTypes.string.isRequired,
    metaBalance: PropTypes.string.isRequired,
    setMetaBalance: PropTypes.func.isRequired,
    pastTransactions: PropTypes.arrayOf(PropTypes.object).isRequired,
    setPastTransactions: PropTypes.func.isRequired,
    userBalances: PropTypes.objectOf(PropTypes.string).isRequired,
    setUserBalances: PropTypes.func.isRequired
  };

  componentDidMount() {
    // Get and set coinbase if it hasn't been set yet
    if (this.props.coinbase.length === 0) {
      web3.eth.getCoinbase().then(coinbase => {
        this.props.setCoinbase(coinbase);
        // Set default address
        web3.eth.defaultAccount = coinbase;
        this.initWeb3Subscriptions();
      });
    } else {
      this.initWeb3Subscriptions();
    }
  }

  onMessageReceived = data => {
    const messages = this.state.messages;
    messages.push(data);
    this.setState({ messages });
  };

  updateWeb3Info = () => {
    const { coinbase, setMetaBalance } = this.props;

    // Get and set user MetaCoin balance
    MetaCoinContract.methods.getBalance(coinbase).call({
      from: coinbase
    }).then(result => {
      setMetaBalance(result);
    }, err => {
      console.error(err);
    });

    // Also load past transactions array
    this.loadPastTransactionsAndAddresses();
  }

  initWeb3Subscriptions = () => {
    const { coinbase, loadContractABI } = this.props;

    loadContractABI('MetaCoin').then(result => {
      // Create an instance of the contract
      MetaCoinContract = new web3.eth.Contract(
        result.abi,
        contractAddress.MetaCoin,
        {
          from: coinbase,
          // gasPrice: '20000000000', // default gas price in wei, 20 gwei in this case
        }
      );

      web3.eth.subscribe('newBlockHeaders', (/* err, results */) => {
        this.updateWeb3Info();
      });

      // Initial call
      this.updateWeb3Info();
    });
  }

  loadPastTransactionsAndAddresses = () => {
    const { setPastTransactions, setUserBalances } = this.props;
    return async.waterfall([
      callback => {
        // First we obtain all of the past transactions events
        MetaCoinContract.getPastEvents('Transfer', {
          fromBlock: 0,
          toBlock: 'latest'
        }, (err, results) => callback(err || (results.length === 0), results));
      },
      (results, callback) => {
        // Save past transactions to redux
        setPastTransactions(results);
        // We also want to create an object of balances and their corresponding
        // balances.
        // First we iterate over all transactions and collect the unique addresses
        const userBalances = {};
        for (let inc = 0; inc < results.length; inc += 1) {
          const txn = results[inc];
          userBalances[txn.returnValues._from.toLowerCase()] = 0;
          userBalances[txn.returnValues._to.toLowerCase()] = 0;
          if (inc === results.length - 1) {
            return callback(null, userBalances);
          }
        }
      },
      (userBalances, callback) => {
        const addresses = Object.keys(userBalances);
        // Now we iterate over each address and obtain each one's balance
        return async.eachSeries(addresses, (address, addrCallback) => ( // Async loop
          setTimeout(() => (/* Trick to prevent strange bluebird.js warning:
                               "a promise was created in a handler but was
                               not returned from it" */
            MetaCoinContract.methods.getBalance(address).call().then(bal => {
              userBalances[address] = bal;
              return addrCallback();
            }, err => addrCallback(err))
          ), 1)
        ), err => callback(err, userBalances));
      }
    ], (err, userBalances) => {
      if (err) {
        throw err;
      }
      setUserBalances(userBalances); // Now we update the redux userBalances
    });
  };

  handleSubmit = event => {
    event.preventDefault();
    const { amountToSend, addressToSend, coinbase } = this.props;

    MetaCoinContract.methods.sendCoin(addressToSend, amountToSend).send({
      from: coinbase
    }).then(() => {}, err => {
      console.error('There was an error sending the transaction!');
      console.error(err);
    });
  };

  render() {
    const {
      coinbase,
      metaBalance,
      amountToSend,
      setAmountToSend,
      addressToSend,
      setAddressToSend,
      pastTransactions,
      userBalances
    } = this.props;
    const style = require('./MetaCoin.scss');

    return (
      <div className={`${style.metacoin} container`}>

MetaCoin (MTC)

You:

            <AddressBlock
              address={coinbase.toLowerCase()}
              balance={metaBalance}
              currency={'MTC'}
            />

          {(pastTransactions.length > 0) &&
Transactions:
}

            {pastTransactions.map(txn =>
              (<li key={`metacoin.txn.${txn.id}`}>
                <div className={`${style.txnAddress}`}>
                  <AddressBlock
                    address={txn.returnValues._from.toLowerCase()}
                    noBalance
                  />

                <div className={`${style.txnAmountWrap}`}>
                  <FontAwesome
                    name="arrow-circle-right"
                    size="2x"
                  />

                    {txn.returnValues._value} MTC

                <div className={`${style.txnAddress}`}>
                  <AddressBlock
                    address={txn.returnValues._to.toLowerCase()}
                    noBalance
                  />

)
            )}

Accounts:

            {Object.keys(userBalances).map(address =>
              (<li key={`metacoin.bals.${address}`}>
                <AddressBlock
                  address={address.toLowerCase()}
                  balance={userBalances[address]}
                  currency={'MTC'}
                />

)
            )}

Send:

          <form onSubmit={this.handleSubmit}>

                <input
                  type="text"
                  placeholder="0x000..."
                  value={addressToSend}
                  onChange={event => {
                    setAddressToSend(event.target.value);
                  }}
                />

                <input
                  type="text"
                  placeholder="0.00"
                  value={amountToSend}
                  onChange={event => {
                    setAmountToSend(event.target.value);
                  }}
                />

                <button className="btn" onClick={this.handleSubmit}>
                  Send
                </button>

          </form>

    );
  }
}
<commit_msg>fix bug in MetaCoin.js getPastEvents callback<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import async from 'async';
import { AddressBlock } from 'components';
import FontAwesome from 'react-fontawesome';
import * as blockchainActions from '../../redux/modules/blockchain';
import * as metaCoinActions from '../../redux/modules/metaCoin';
import web3 from '../../web3';
import contractAddress from '../../../contractAddresses';

let MetaCoinContract;

@connect(state => (
  {
    // user: state.auth.user,
    coinbase: state.blockchain.coinbase,
    amountToSend: state.metaCoin.amountToSend,
    addressToSend: state.metaCoin.addressToSend,
    metaBalance: state.metaCoin.metaBalance,
    pastTransactions: state.metaCoin.pastTransactions,
    userBalances: state.metaCoin.userBalances
  }),
{
  ...blockchainActions,
  ...metaCoinActions
}
)
export default class MetaCoin extends Component {
  static propTypes = {
    // user: PropTypes.shape({
    //   email: PropTypes.string
    // }),
    coinbase: PropTypes.string.isRequired,
    setCoinbase: PropTypes.func.isRequired,
    loadContractABI: PropTypes.func.isRequired,
    setAmountToSend: PropTypes.func.isRequired,
    setAddressToSend: PropTypes.func.isRequired,
    amountToSend: PropTypes.string.isRequired,
    addressToSend: PropTypes.string.isRequired,
    metaBalance: PropTypes.string.isRequired,
    setMetaBalance: PropTypes.func.isRequired,
    pastTransactions: PropTypes.arrayOf(PropTypes.object).isRequired,
    setPastTransactions: PropTypes.func.isRequired,
    userBalances: PropTypes.objectOf(PropTypes.string).isRequired,
    setUserBalances: PropTypes.func.isRequired
  };

  componentDidMount() {
    // Get and set coinbase if it hasn't been set yet
    if (this.props.coinbase.length === 0) {
      web3.eth.getCoinbase().then(coinbase => {
        this.props.setCoinbase(coinbase);
        // Set default address
        web3.eth.defaultAccount = coinbase;
        this.initWeb3Subscriptions();
      });
    } else {
      this.initWeb3Subscriptions();
    }
  }

  onMessageReceived = data => {
    const messages = this.state.messages;
    messages.push(data);
    this.setState({ messages });
  };

  updateWeb3Info = () => {
    const { coinbase, setMetaBalance } = this.props;

    // Get and set user MetaCoin balance
    MetaCoinContract.methods.getBalance(coinbase).call({
      from: coinbase
    }).then(result => {
      setMetaBalance(result);
    }, err => {
      console.error(err);
    });

    // Also load past transactions array
    this.loadPastTransactionsAndAddresses();
  }

  initWeb3Subscriptions = () => {
    const { coinbase, loadContractABI } = this.props;

    loadContractABI('MetaCoin').then(result => {
      // Create an instance of the contract
      MetaCoinContract = new web3.eth.Contract(
        result.abi,
        contractAddress.MetaCoin,
        {
          from: coinbase,
          // gasPrice: '20000000000', // default gas price in wei, 20 gwei in this case
        }
      );

      web3.eth.subscribe('newBlockHeaders', (/* err, results */) => {
        this.updateWeb3Info();
      });

      // Initial call
      this.updateWeb3Info();
    });
  }

  loadPastTransactionsAndAddresses = () => {
    const { setPastTransactions, setUserBalances } = this.props;
    return async.waterfall([
      callback => {
        // First we obtain all of the past transactions events
        MetaCoinContract.getPastEvents('Transfer', {
          fromBlock: 0,
          toBlock: 'latest'
        }, (err, results) => callback(err, results));
      },
      (results, callback) => {
        // Save past transactions to redux
        setPastTransactions(results);
        // Check if the results were empty (in which case the loop won't run)
        if (results.length === 0) {
          return callback(null, {});
        }
        // We also want to create an object of balances and their corresponding
        // balances.
        // First we iterate over all transactions and collect the unique addresses
        const userBalances = {};
        for (let inc = 0; inc < results.length; inc += 1) {
          const txn = results[inc];
          userBalances[txn.returnValues._from.toLowerCase()] = 0;
          userBalances[txn.returnValues._to.toLowerCase()] = 0;
          if (inc === results.length - 1) {
            return callback(null, userBalances);
          }
        }
      },
      (userBalances, callback) => {
        const addresses = Object.keys(userBalances);
        // Check if addresses is empty (in which case the loop won't run)
        if (addresses.length === 0) {
          return callback(null, {});
        }
        // Now we iterate over each address and obtain each one's balance
        return async.eachSeries(addresses, (address, addrCallback) => ( // Async loop
          setTimeout(() => (/* Trick to prevent strange bluebird.js warning:
                               "a promise was created in a handler but was
                               not returned from it" */
            MetaCoinContract.methods.getBalance(address).call().then(bal => {
              userBalances[address] = bal;
              return addrCallback();
            }, err => addrCallback(err))
          ), 1)
        ), err => callback(err, userBalances));
      }
    ], (err, userBalances) => {
      if (err) {
        throw err;
      }
      setUserBalances(userBalances); // Now we update the redux userBalances
    });
  };

  handleSubmit = event => {
    event.preventDefault();
    const { amountToSend, addressToSend, coinbase } = this.props;

    MetaCoinContract.methods.sendCoin(addressToSend, amountToSend).send({
      from: coinbase
    }).then(() => {}, err => {
      console.error('There was an error sending the transaction!');
      console.error(err);
    });
  };

  render() {
    const {
      coinbase,
      metaBalance,
      amountToSend,
      setAmountToSend,
      addressToSend,
      setAddressToSend,
      pastTransactions,
      userBalances
    } = this.props;
    const style = require('./MetaCoin.scss');

    return (
      <div className={`${style.metacoin} container`}>

MetaCoin (MTC)

You:

            <AddressBlock
              address={coinbase.toLowerCase()}
              balance={metaBalance}
              currency={'MTC'}
            />

          {(pastTransactions.length > 0) &&
Transactions:
}

            {pastTransactions.map(txn =>
              (<li key={`metacoin.txn.${txn.id}`}>
                <div className={`${style.txnAddress}`}>
                  <AddressBlock
                    address={txn.returnValues._from.toLowerCase()}
                    noBalance
                  />

                <div className={`${style.txnAmountWrap}`}>
                  <FontAwesome
                    name="arrow-circle-right"
                    size="2x"
                  />

                    {txn.returnValues._value} MTC

                <div className={`${style.txnAddress}`}>
                  <AddressBlock
                    address={txn.returnValues._to.toLowerCase()}
                    noBalance
                  />

)
            )}

Accounts:

            {Object.keys(userBalances).map(address =>
              (<li key={`metacoin.bals.${address}`}>
                <AddressBlock
                  address={address.toLowerCase()}
                  balance={userBalances[address]}
                  currency={'MTC'}
                />

)
            )}

Send:

          <form onSubmit={this.handleSubmit}>

                <input
                  type="text"
                  placeholder="0x000..."
                  value={addressToSend}
                  onChange={event => {
                    setAddressToSend(event.target.value);
                  }}
                />

                <input
                  type="text"
                  placeholder="0.00"
                  value={amountToSend}
                  onChange={event => {
                    setAmountToSend(event.target.value);
                  }}
                />

                <button className="btn" onClick={this.handleSubmit}>
                  Send
                </button>

          </form>

    );
  }
}
<|endoftext|>


<commit_before>"use strict";

module.exports = function(grunt) {
  require("time-grunt")(grunt);
  require('load-grunt-tasks')(grunt, {
    pattern: [
      'grunt-*',
      '!grunt-configure'
    ]
  });

  grunt.initConfig(
    require("grunt-configure")("./etc/grunt/*.{js,json,yml}")
  );

  grunt.registerTask("build",   [ "psc:main", "dotPsci" ]);
  grunt.registerTask("test",    [ "psc:test", "execute:test" ]);
  grunt.registerTask("dev",     [ "build", "watch" ]);
  grunt.registerTask("default", [ "build" ]);
};
<commit_msg>remove build step<commit_after>"use strict";

module.exports = function(grunt) {
  require("time-grunt")(grunt);
  require('load-grunt-tasks')(grunt, {
    pattern: [
      'grunt-*',
      '!grunt-configure'
    ]
  });

  grunt.initConfig(
    require("grunt-configure")("./etc/grunt/*.{js,json,yml}")
  );

  grunt.registerTask("build",   [ "dotPsci" ]);
  grunt.registerTask("test",    [ "psc:test", "execute:test" ]);
  grunt.registerTask("dev",     [ "build", "watch" ]);
  grunt.registerTask("default", [ "build" ]);
};
<|endoftext|>


<commit_before>module.exports = function (grunt) {
  'use strict';

  /**
   * read optional JSON from filepath
   * @param {String} filepath
   * @return {Object}
   */
  var readOptionalJSON = function (filepath) {
    var data = {};
    try {
      data = grunt.file.readJSON(filepath);
      // The concatenated file won't pass onevar
      // But our modules can
      delete data.onever;
    } catch (e) { }
    return data;
  };

  var customLaunchers = {
    /*
    'SL_IE8': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '8.0',
      platform: 'windows XP'
    },
    */
    'SL_IE9': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '9.0',
      platform: 'windows 7'
    },
    'SL_IE10': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '10.0',
      platform: 'windows 8'
    },
    'SL_IE11': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '11.0',
      platform: 'windows 8.1'
    },
    'SL_CHROME': {
      base: 'SauceLabs',
      browserName: 'chrome',
      version: '43',
      platform: 'windows 8'
    },
    'SL_FIREFOX': {
      base: 'SauceLabs',
      browserName: 'firefox',
      version: '38',
      platform: 'windows 8'
    },
    'SL_SAFARI': {
      base: 'SauceLabs',
      browserName: 'safari',
      version: '8.0',
      platform: 'OS X 10.10'
    }
  };

  grunt.initConfig({
    // package File
    pkg: grunt.file.readJSON('package.json'),

    // bulid source(grunt-build.js).
    build: {
      all: {
        baseUrl: 'src/js',        // base url
        startFile: 'intro.js',    // intro part
        endFile: 'outro.js',      // outro part
        outFile: 'dist/summernote.js' // out file
      }
    },

    // for javascript convention.
    jshint: {
      all: {
        src: [
          'src/**/*.js',
          'plugin/**/*.js',
          'lang/**/*.js',
          'Gruntfile.js',
          'test/**/*.js',
          '!test/coverage/**/*.js',
          'build/*.js'
        ],
        options: {
          jshintrc: true
        }
      },
      dist: {
        src: 'dist/summernote.js',
        options: readOptionalJSON('.jshintrc')
      }
    },

    jscs: {
      src: 'src',
      gruntfile: 'Gruntfile.js',
      build: 'build'
    },

    // uglify: minify javascript
    uglify: {
      options: {
        banner: '/*! Summernote v<%=pkg.version%> | (c) 2013-2015 Alan Hong and other contributors | MIT license */\n'
      },
      all: {
        files: [
          { 'dist/summernote.min.js': ['dist/summernote.js'] },
          {
            expand: true,
            cwd: 'dist/lang',
            src: '**/*.js',
            dest: 'dist/lang',
            ext: '.min.js'
          }
        ]
      }
    },

    // recess: minify stylesheets
    recess: {
      dist: {
        options: { compile: true, compress: true },
        files: {
          'dist/summernote.css': ['src/less/summernote.less']
        }
      }
    },

    // compress: summernote-{{version}}-dist.zip
    compress: {
      main: {
        options: {
          archive: function () {
            return 'dist/summernote-{{version}}-dist.zip'.replace(
              '{{version}}',
              grunt.config('pkg.version')
            );
          }
        },
        files: [{
          expand: true,
          src: [
            'dist/*.js',
            'dist/*.css'
          ]
        }, {
          src: ['plugin/**/*.js', 'lang/**/*.js'],
          dest: 'dist/'
        }]
      }
    },

    // connect configuration.
    connect: {
      all: {
        options: {
          port: 3000
        }
      }
    },

    // watch source code change
    watch: {
      all: {
        files: ['src/less/*.less', 'src/js/**/*.js'],
        tasks: ['recess', 'lint'],
        options: {
          livereload: true
        }
      }
    },

    // Meteor commands to test and publish package
    exec: {
      'meteor-test': {
        command: 'meteor/runtests.sh'
      },
      'meteor-publish': {
        command: 'meteor/publish.sh'
      }
    },

    karma: {
      options: {
        configFile: './test/karma.conf.js'
      },
      all: {
        // Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS, IE
        browsers: ['PhantomJS'],
        reporters: ['progress']
      },
      dist: {
        singleRun: true,
        browsers: ['PhantomJS']
      },
      travis: {
        singleRun: true,
        browsers: ['PhantomJS'],
        reporters: ['progress', 'coverage']
      },
      saucelabs: {
        reporters: ['saucelabs'],
        sauceLabs: {
          testName: '[Travis] unit tests for summernote',
          startConnect: false,
          tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
          build: process.env.TRAVIS_BUILD_NUMBER,
          tags: [process.env.TRAVIS_BRANCH, process.env.TRAVIS_PULL_REQUEST]
        },
        captureTimeout: 120000,
        customLaunchers: customLaunchers,
        browsers: Object.keys(customLaunchers),
        singleRun: true
      }
    },

    coveralls: {
      options: {
        force: false
      },
      travis: {
        src: 'test/coverage/**/lcov.info'
      }
    },
    clean: {
      dist: ['dist']
    },
    copy: {
      dist: {
        files: [
          {src: 'lang/*', dest: 'dist/'}
        ]
      }
    }
  });

  // load all tasks from the grunt plugins used in this file
  require('load-grunt-tasks')(grunt);

  // load all grunts/*.js
  grunt.loadTasks('grunts');

  // server: runt server for development
  grunt.registerTask('server', ['connect', 'watch']);

  // lint
  grunt.registerTask('lint', ['jshint', 'jscs']);

  // test: unit test on test folder
  grunt.registerTask('test', ['lint', 'karma:all']);

  // test: unit test on travis
  grunt.registerTask('test-travis', ['lint', 'karma:travis']);

  // test: saucelabs test
  grunt.registerTask('saucelabs-test', ['karma:saucelabs']);

  // dist: make dist files
  grunt.registerTask('dist', [
    'clean:dist',
    'build', 'jshint', 'karma:dist',
    'copy:dist', 'uglify', 'recess', 'compress'
  ]);

  // default: server
  grunt.registerTask('default', ['server']);

  // Meteor tasks
  grunt.registerTask('meteor-test', 'exec:meteor-test');
  grunt.registerTask('meteor-publish', 'exec:meteor-publish');
  grunt.registerTask('meteor', ['meteor-test', 'meteor-publish']);

};
<commit_msg>Reload when test file is changed.<commit_after>module.exports = function (grunt) {
  'use strict';

  /**
   * read optional JSON from filepath
   * @param {String} filepath
   * @return {Object}
   */
  var readOptionalJSON = function (filepath) {
    var data = {};
    try {
      data = grunt.file.readJSON(filepath);
      // The concatenated file won't pass onevar
      // But our modules can
      delete data.onever;
    } catch (e) { }
    return data;
  };

  var customLaunchers = {
    /*
    'SL_IE8': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '8.0',
      platform: 'windows XP'
    },
    */
    'SL_IE9': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '9.0',
      platform: 'windows 7'
    },
    'SL_IE10': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '10.0',
      platform: 'windows 8'
    },
    'SL_IE11': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '11.0',
      platform: 'windows 8.1'
    },
    'SL_CHROME': {
      base: 'SauceLabs',
      browserName: 'chrome',
      version: '43',
      platform: 'windows 8'
    },
    'SL_FIREFOX': {
      base: 'SauceLabs',
      browserName: 'firefox',
      version: '38',
      platform: 'windows 8'
    },
    'SL_SAFARI': {
      base: 'SauceLabs',
      browserName: 'safari',
      version: '8.0',
      platform: 'OS X 10.10'
    }
  };

  grunt.initConfig({
    // package File
    pkg: grunt.file.readJSON('package.json'),

    // bulid source(grunt-build.js).
    build: {
      all: {
        baseUrl: 'src/js',        // base url
        startFile: 'intro.js',    // intro part
        endFile: 'outro.js',      // outro part
        outFile: 'dist/summernote.js' // out file
      }
    },

    // for javascript convention.
    jshint: {
      all: {
        src: [
          'src/**/*.js',
          'plugin/**/*.js',
          'lang/**/*.js',
          'Gruntfile.js',
          'test/**/*.js',
          '!test/coverage/**/*.js',
          'build/*.js'
        ],
        options: {
          jshintrc: true
        }
      },
      dist: {
        src: 'dist/summernote.js',
        options: readOptionalJSON('.jshintrc')
      }
    },

    jscs: {
      src: 'src',
      gruntfile: 'Gruntfile.js',
      build: 'build'
    },

    // uglify: minify javascript
    uglify: {
      options: {
        banner: '/*! Summernote v<%=pkg.version%> | (c) 2013-2015 Alan Hong and other contributors | MIT license */\n'
      },
      all: {
        files: [
          { 'dist/summernote.min.js': ['dist/summernote.js'] },
          {
            expand: true,
            cwd: 'dist/lang',
            src: '**/*.js',
            dest: 'dist/lang',
            ext: '.min.js'
          }
        ]
      }
    },

    // recess: minify stylesheets
    recess: {
      dist: {
        options: { compile: true, compress: true },
        files: {
          'dist/summernote.css': ['src/less/summernote.less']
        }
      }
    },

    // compress: summernote-{{version}}-dist.zip
    compress: {
      main: {
        options: {
          archive: function () {
            return 'dist/summernote-{{version}}-dist.zip'.replace(
              '{{version}}',
              grunt.config('pkg.version')
            );
          }
        },
        files: [{
          expand: true,
          src: [
            'dist/*.js',
            'dist/*.css'
          ]
        }, {
          src: ['plugin/**/*.js', 'lang/**/*.js'],
          dest: 'dist/'
        }]
      }
    },

    // connect configuration.
    connect: {
      all: {
        options: {
          port: 3000
        }
      }
    },

    // watch source code change
    watch: {
      all: {
        files: ['src/less/*.less', 'src/js/**/*.js', 'test/unit/**/*.js'],
        tasks: ['recess', 'lint'],
        options: {
          livereload: true
        }
      }
    },

    // Meteor commands to test and publish package
    exec: {
      'meteor-test': {
        command: 'meteor/runtests.sh'
      },
      'meteor-publish': {
        command: 'meteor/publish.sh'
      }
    },

    karma: {
      options: {
        configFile: './test/karma.conf.js'
      },
      all: {
        // Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS, IE
        browsers: ['PhantomJS'],
        reporters: ['progress']
      },
      dist: {
        singleRun: true,
        browsers: ['PhantomJS']
      },
      travis: {
        singleRun: true,
        browsers: ['PhantomJS'],
        reporters: ['progress', 'coverage']
      },
      saucelabs: {
        reporters: ['saucelabs'],
        sauceLabs: {
          testName: '[Travis] unit tests for summernote',
          startConnect: false,
          tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
          build: process.env.TRAVIS_BUILD_NUMBER,
          tags: [process.env.TRAVIS_BRANCH, process.env.TRAVIS_PULL_REQUEST]
        },
        captureTimeout: 120000,
        customLaunchers: customLaunchers,
        browsers: Object.keys(customLaunchers),
        singleRun: true
      }
    },

    coveralls: {
      options: {
        force: false
      },
      travis: {
        src: 'test/coverage/**/lcov.info'
      }
    },
    clean: {
      dist: ['dist']
    },
    copy: {
      dist: {
        files: [
          {src: 'lang/*', dest: 'dist/'}
        ]
      }
    }
  });

  // load all tasks from the grunt plugins used in this file
  require('load-grunt-tasks')(grunt);

  // load all grunts/*.js
  grunt.loadTasks('grunts');

  // server: runt server for development
  grunt.registerTask('server', ['connect', 'watch']);

  // lint
  grunt.registerTask('lint', ['jshint', 'jscs']);

  // test: unit test on test folder
  grunt.registerTask('test', ['lint', 'karma:all']);

  // test: unit test on travis
  grunt.registerTask('test-travis', ['lint', 'karma:travis']);

  // test: saucelabs test
  grunt.registerTask('saucelabs-test', ['karma:saucelabs']);

  // dist: make dist files
  grunt.registerTask('dist', [
    'clean:dist',
    'build', 'jshint', 'karma:dist',
    'copy:dist', 'uglify', 'recess', 'compress'
  ]);

  // default: server
  grunt.registerTask('default', ['server']);

  // Meteor tasks
  grunt.registerTask('meteor-test', 'exec:meteor-test');
  grunt.registerTask('meteor-publish', 'exec:meteor-publish');
  grunt.registerTask('meteor', ['meteor-test', 'meteor-publish']);

};
<|endoftext|>


<commit_before>import { waitFor } from '../dispatcher/Dispatcher';
import { mergeIntoBag } from '../utils/StoreUtils';
import ActionTypes from '../constants/ActionTypes';
import { API_URLS } from '../constants/Constants';
import selectn from 'selectn';
import UserStore from './UserStore';
import LoginStore from './LoginStore';
import { getValidationErrors } from '../utils/StoreUtils';
import BaseStore from './BaseStore';

class QuestionStore extends BaseStore {
    setInitial() {
        super.setInitial();
        this._registerQuestionsLength = 4;
        this._questions = {};
        this._initialPaginationUrl = API_URLS.ANSWERS;
        this._initialComparedPaginationUrl = API_URLS.COMPARED_ANSWERS;
        this._answerQuestion = {};
        this._answersLength = [];
        this._errors = '';
        this._noMoreQuestions = false;
        this._goToQuestionStats = false;
        this._isJustCompleted = false;
        this._loadingComparedQuestions = false;
        this._loadingOwnQuestions = false;
        this._isRequestedQuestion = {};
        this._comparedOrder = {};
    }

    _registerToActions(action) {
        waitFor([UserStore.dispatchToken, LoginStore.dispatchToken]);
        super._registerToActions(action);
        let newItems = {};
        switch (action.type) {
            case ActionTypes.REQUEST_QUESTIONS:
                this._noMoreQuestions = false;
                this._loadingOwnQuestions = true;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_COMPARED_QUESTIONS:
                this._loadingComparedQuestions = true;
                this._noMoreQuestions = false;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTION:
                this._isRequestedQuestion[action.otherUserId ? action.otherUserId : action.userId] = true;
                break;
            case ActionTypes.REQUEST_EXISTING_QUESTION:
                this._answerQuestion = {};
                this._answerQuestion[action.questionId] = this._questions[action.userId][action.questionId].question;
                this.emitChange();
                break;
            case ActionTypes.REMOVE_PREVIOUS_QUESTION:
                this._answerQuestion = {};
                this.emitChange();
                break;
            case ActionTypes.ANSWER_QUESTION:
                break;
            case ActionTypes.SKIP_QUESTION:
                break;
            case ActionTypes.SET_QUESTION_EDITABLE:
                this.setEditable(action.questionId);
                this.emitChange();
                break;
            case ActionTypes.QUESTIONS_POPUP_DISPLAYED:
                this._isJustCompleted = false;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTIONS_SUCCESS:
                newItems[action.userId] = action.response.entities.items;
                this._pagination[action.userId] = action.response.result.pagination;
                this._loadingOwnQuestions = false;
                mergeIntoBag(this._questions, newItems);
                this.emitChange();
                break;
            case ActionTypes.REQUEST_COMPARED_QUESTIONS_SUCCESS:
                let items = action.response.items;
                const otherUserId = action.otherUserId;
                Object.keys(items).forEach(index => {
                    const userId = items[index].userId;
                    const questions = items[index].questions || [];
                    this._setQuestionsOrder(otherUserId, questions);

                    newItems[userId] = questions;
                });
                this._pagination[otherUserId] = action.response.pagination;
                this._loadingComparedQuestions = false;

                mergeIntoBag(this._questions, newItems);
                this.emitChange();
                break;
            case ActionTypes.REQUEST_LOGIN_USER_SUCCESS:
            case ActionTypes.REQUEST_AUTOLOGIN_SUCCESS:
                this._answersLength = parseInt(action.response.questionsTotal);
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTION_SUCCESS:
                this._answerQuestion = action.response.entities.question;
                this.emitChange();
                break;
            case ActionTypes.ANSWER_QUESTION_SUCCESS:
                const userAnswer = action.response.userAnswer;
                const userAnswerQuestion = action.response.question;
                this._questions[action.userId] = this._questions[action.userId] || {};
                this._questions[action.userId][userAnswer.questionId] = {question: userAnswerQuestion, userAnswer: userAnswer};
                this._goToQuestionStats = true;
                this._pagination[action.userId] = this._pagination[action.userId] || {};
                this._pagination[action.userId].total++;
                this._answersLength++;
                this._isJustCompleted = this.ownAnswersLength(action.userId) === this._registerQuestionsLength;
                this.emitChange();
                break;
            case ActionTypes.SKIP_QUESTION_SUCCESS:
                Object.keys(this._questions).forEach(questionUserId => {
                    if (this._questions[questionUserId][action.questionId]) {
                        delete this._questions[questionUserId][action.questionId];
                        this._pagination[questionUserId].total--;
                    }
                });
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTIONS_ERROR:
                this._loadingOwnQuestions = false;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_COMPARED_QUESTIONS_ERROR:
                break;
            case ActionTypes.REQUEST_QUESTION_ERROR:
                this._noMoreQuestions = true;
                this.emitChange();
                break;
            case ActionTypes.ANSWER_QUESTION_ERROR:
                this._errors = getValidationErrors(action.error);
                this.emitChange();
                break;
            case ActionTypes.SKIP_QUESTION_ERROR:
                break;
            case ActionTypes.REQUEST_STATS_SUCCESS:
                this._answersLength = action.response.numberOfQuestionsAnswered;
                this.emitChange();
        }
    }

    contains(userId, questionId) {
        let question = selectn(userId + '.' + questionId, this._questions);
        return question && question.hasOwnProperty('question');
    }

    get(userId) {
        return this._questions[userId];
    }

    getCompared(otherUserId) {
        const questions = this._questions[otherUserId] ? Object.assign({}, this._questions[otherUserId]) : {};

        let orderedQuestions = {};
        Object.keys(questions).forEach((questionId) => {
            const position = this._comparedOrder[otherUserId][questionId];
            orderedQuestions[position] = questions[questionId];
        });

        return orderedQuestions;
    }

    setEditable(questionId) {
        let userId = LoginStore.user.id;
        if (this._questions[userId][questionId]) {
            this._questions[userId][questionId].userAnswer.isEditable = true;
        }
    }

    isFirstQuestion(userId) {
        return !this._questions.hasOwnProperty(userId) ||
            this._questions.hasOwnProperty(userId) && Object.keys(this._questions[userId]).length === 0;
    }

    getPagination(userId) {
        return this._pagination[userId] || {};
    }

    getRequestQuestionsUrl(userId) {
        return this.getPaginationUrl(userId, this._initialPaginationUrl);
    }

    getRequestComparedQuestionsUrl(userId, filters) {
        let url = this.getPaginationUrl(userId, this._initialComparedPaginationUrl);
        if (url === this._initialComparedPaginationUrl) {
            url = url.replace('{otherUserId}', userId);
            url = url + filters.map(filter => '&' + filter + '=1');
        }
        return url;
    }

    getQuestion() {
        return this.first(this._answerQuestion);
    }

    hasQuestion() {
        return !!this.getQuestion();
    }

    getErrors() {
        return this._errors;
    }

    noMoreQuestions() {
        return this._noMoreQuestions;
    }

    getUserAnswer(userId, questionId) {
        return this._questions[userId] ? selectn('userAnswer', this._questions[userId][questionId]) : null;
    }

    mustGoToQuestionStats() {
        const goToQuestionStats = this._goToQuestionStats;
        this._goToQuestionStats = false;
        return goToQuestionStats;
    }

    isJustRegistered(userId) {
        return this.ownAnswersLength(userId) < this._registerQuestionsLength;
    }

    isJustCompleted() {
        return this._isJustCompleted;
    }

    ownAnswersLength(userId) {
        return this._answersLength > 0 ? this._answersLength : this.otherAnswersLength(userId);
    }

    otherAnswersLength(userId) {
        return this.getPagination(userId).hasOwnProperty('total') ? this.getPagination(userId).total :
            Array.isArray(this._questions[userId]) ? Object.keys(this._questions[userId]).length : 0;
    }

    registerQuestionsLength() {
        return this._registerQuestionsLength;
    }

    first(obj) {
        for (let a in obj) {
            if (obj.hasOwnProperty(a)) {
                return obj[a];
            }
        }
    }

    isLoadingComparedQuestions() {
        return this._loadingComparedQuestions;
    }

    isLoadingOwnQuestions() {
        return this._loadingOwnQuestions;
    }

    isRequestedQuestion(userId) {
        return this._isRequestedQuestion[userId] || false;
    }

    _setQuestionsOrder(userId, questions) {
        let sortable = Object.keys(questions).map((questionId) => {
            return questions[questionId];
        });

        //sort by id first to keep same order
        sortable = sortable.sort((questionA, questionB) => {
            return questionA.question.questionId - questionB.question.questionId;
        });

        sortable = sortable.sort((questionA, questionB) => {
            const questionApriority = questionA.question.hasOwnProperty('isCommon') && questionA.question.isCommon === true ? 1 : 0;
            const questionBpriority = questionB.question.hasOwnProperty('isCommon') && questionB.question.isCommon === true ? 1 : 0;
            return questionBpriority - questionApriority;
        });

        this._comparedOrder[userId] = this._comparedOrder[userId] ? this._comparedOrder[userId] : {};
        const comparedOrderLength = Object.keys(this._comparedOrder[userId]).length;
        sortable.forEach((question, index) => {
            this._comparedOrder[userId][question.question.questionId] = this._comparedOrder[userId][question.question.questionId] !== undefined ? this._comparedOrder[userId][question.question.questionId] : index + comparedOrderLength;
        });
    }

}

export default new QuestionStore();
<commit_msg>Make sure next question is loaded after answering a question<commit_after>import { waitFor } from '../dispatcher/Dispatcher';
import { mergeIntoBag } from '../utils/StoreUtils';
import ActionTypes from '../constants/ActionTypes';
import { API_URLS } from '../constants/Constants';
import selectn from 'selectn';
import UserStore from './UserStore';
import LoginStore from './LoginStore';
import { getValidationErrors } from '../utils/StoreUtils';
import BaseStore from './BaseStore';

class QuestionStore extends BaseStore {
    setInitial() {
        super.setInitial();
        this._registerQuestionsLength = 4;
        this._questions = {};
        this._initialPaginationUrl = API_URLS.ANSWERS;
        this._initialComparedPaginationUrl = API_URLS.COMPARED_ANSWERS;
        this._answerQuestion = {};
        this._answersLength = [];
        this._errors = '';
        this._noMoreQuestions = false;
        this._goToQuestionStats = false;
        this._isJustCompleted = false;
        this._loadingComparedQuestions = false;
        this._loadingOwnQuestions = false;
        this._isRequestedQuestion = {};
        this._comparedOrder = {};
    }

    _registerToActions(action) {
        waitFor([UserStore.dispatchToken, LoginStore.dispatchToken]);
        super._registerToActions(action);
        let newItems = {};
        switch (action.type) {
            case ActionTypes.REQUEST_QUESTIONS:
                this._noMoreQuestions = false;
                this._loadingOwnQuestions = true;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_COMPARED_QUESTIONS:
                this._loadingComparedQuestions = true;
                this._noMoreQuestions = false;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTION:
                this._isRequestedQuestion[action.otherUserId ? action.otherUserId : action.userId] = true;
                break;
            case ActionTypes.REQUEST_EXISTING_QUESTION:
                this._answerQuestion = {};
                this._answerQuestion[action.questionId] = this._questions[action.userId][action.questionId].question;
                this.emitChange();
                break;
            case ActionTypes.REMOVE_PREVIOUS_QUESTION:
                this._answerQuestion = {};
                this.emitChange();
                break;
            case ActionTypes.ANSWER_QUESTION:
                break;
            case ActionTypes.SKIP_QUESTION:
                break;
            case ActionTypes.SET_QUESTION_EDITABLE:
                this.setEditable(action.questionId);
                this.emitChange();
                break;
            case ActionTypes.QUESTIONS_POPUP_DISPLAYED:
                this._isJustCompleted = false;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTIONS_SUCCESS:
                newItems[action.userId] = action.response.entities.items;
                this._pagination[action.userId] = action.response.result.pagination;
                this._loadingOwnQuestions = false;
                mergeIntoBag(this._questions, newItems);
                this.emitChange();
                break;
            case ActionTypes.REQUEST_COMPARED_QUESTIONS_SUCCESS:
                let items = action.response.items;
                const otherUserId = action.otherUserId;
                Object.keys(items).forEach(index => {
                    const userId = items[index].userId;
                    const questions = items[index].questions || [];
                    this._setQuestionsOrder(otherUserId, questions);

                    newItems[userId] = questions;
                });
                this._pagination[otherUserId] = action.response.pagination;
                this._loadingComparedQuestions = false;

                mergeIntoBag(this._questions, newItems);
                this.emitChange();
                break;
            case ActionTypes.REQUEST_LOGIN_USER_SUCCESS:
            case ActionTypes.REQUEST_AUTOLOGIN_SUCCESS:
                this._answersLength = parseInt(action.response.questionsTotal);
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTION_SUCCESS:
                this._answerQuestion = action.response.entities.question;
                this.emitChange();
                break;
            case ActionTypes.ANSWER_QUESTION_SUCCESS:
                const userAnswer = action.response.userAnswer;
                const userAnswerQuestion = action.response.question;
                this._questions[action.userId] = this._questions[action.userId] || {};
                this._questions[action.userId][userAnswer.questionId] = {question: userAnswerQuestion, userAnswer: userAnswer};
                this._goToQuestionStats = true;
                this._pagination[action.userId] = this._pagination[action.userId] || {};
                this._pagination[action.userId].total++;
                this._answersLength++;
                this._isJustCompleted = this.ownAnswersLength(action.userId) === this._registerQuestionsLength;
                this._isRequestedQuestion = {};
                this.emitChange();
                break;
            case ActionTypes.SKIP_QUESTION_SUCCESS:
                Object.keys(this._questions).forEach(questionUserId => {
                    if (this._questions[questionUserId][action.questionId]) {
                        delete this._questions[questionUserId][action.questionId];
                        this._pagination[questionUserId].total--;
                    }
                });
                this.emitChange();
                break;
            case ActionTypes.REQUEST_QUESTIONS_ERROR:
                this._loadingOwnQuestions = false;
                this.emitChange();
                break;
            case ActionTypes.REQUEST_COMPARED_QUESTIONS_ERROR:
                break;
            case ActionTypes.REQUEST_QUESTION_ERROR:
                this._noMoreQuestions = true;
                this.emitChange();
                break;
            case ActionTypes.ANSWER_QUESTION_ERROR:
                this._errors = getValidationErrors(action.error);
                this.emitChange();
                break;
            case ActionTypes.SKIP_QUESTION_ERROR:
                break;
            case ActionTypes.REQUEST_STATS_SUCCESS:
                this._answersLength = action.response.numberOfQuestionsAnswered;
                this.emitChange();
        }
    }

    contains(userId, questionId) {
        let question = selectn(userId + '.' + questionId, this._questions);
        return question && question.hasOwnProperty('question');
    }

    get(userId) {
        return this._questions[userId];
    }

    getCompared(otherUserId) {
        const questions = this._questions[otherUserId] ? Object.assign({}, this._questions[otherUserId]) : {};

        let orderedQuestions = {};
        Object.keys(questions).forEach((questionId) => {
            const position = this._comparedOrder[otherUserId][questionId];
            orderedQuestions[position] = questions[questionId];
        });

        return orderedQuestions;
    }

    setEditable(questionId) {
        let userId = LoginStore.user.id;
        if (this._questions[userId][questionId]) {
            this._questions[userId][questionId].userAnswer.isEditable = true;
        }
    }

    isFirstQuestion(userId) {
        return !this._questions.hasOwnProperty(userId) ||
            this._questions.hasOwnProperty(userId) && Object.keys(this._questions[userId]).length === 0;
    }

    getPagination(userId) {
        return this._pagination[userId] || {};
    }

    getRequestQuestionsUrl(userId) {
        return this.getPaginationUrl(userId, this._initialPaginationUrl);
    }

    getRequestComparedQuestionsUrl(userId, filters) {
        let url = this.getPaginationUrl(userId, this._initialComparedPaginationUrl);
        if (url === this._initialComparedPaginationUrl) {
            url = url.replace('{otherUserId}', userId);
            url = url + filters.map(filter => '&' + filter + '=1');
        }
        return url;
    }

    getQuestion() {
        return this.first(this._answerQuestion);
    }

    hasQuestion() {
        return !!this.getQuestion();
    }

    getErrors() {
        return this._errors;
    }

    noMoreQuestions() {
        return this._noMoreQuestions;
    }

    getUserAnswer(userId, questionId) {
        return this._questions[userId] ? selectn('userAnswer', this._questions[userId][questionId]) : null;
    }

    mustGoToQuestionStats() {
        const goToQuestionStats = this._goToQuestionStats;
        this._goToQuestionStats = false;
        return goToQuestionStats;
    }

    isJustRegistered(userId) {
        return this.ownAnswersLength(userId) < this._registerQuestionsLength;
    }

    isJustCompleted() {
        return this._isJustCompleted;
    }

    ownAnswersLength(userId) {
        return this._answersLength > 0 ? this._answersLength : this.otherAnswersLength(userId);
    }

    otherAnswersLength(userId) {
        return this.getPagination(userId).hasOwnProperty('total') ? this.getPagination(userId).total :
            Array.isArray(this._questions[userId]) ? Object.keys(this._questions[userId]).length : 0;
    }

    registerQuestionsLength() {
        return this._registerQuestionsLength;
    }

    first(obj) {
        for (let a in obj) {
            if (obj.hasOwnProperty(a)) {
                return obj[a];
            }
        }
    }

    isLoadingComparedQuestions() {
        return this._loadingComparedQuestions;
    }

    isLoadingOwnQuestions() {
        return this._loadingOwnQuestions;
    }

    isRequestedQuestion(userId) {
        return this._isRequestedQuestion[userId] || false;
    }

    _setQuestionsOrder(userId, questions) {
        let sortable = Object.keys(questions).map((questionId) => {
            return questions[questionId];
        });

        //sort by id first to keep same order
        sortable = sortable.sort((questionA, questionB) => {
            return questionA.question.questionId - questionB.question.questionId;
        });

        sortable = sortable.sort((questionA, questionB) => {
            const questionApriority = questionA.question.hasOwnProperty('isCommon') && questionA.question.isCommon === true ? 1 : 0;
            const questionBpriority = questionB.question.hasOwnProperty('isCommon') && questionB.question.isCommon === true ? 1 : 0;
            return questionBpriority - questionApriority;
        });

        this._comparedOrder[userId] = this._comparedOrder[userId] ? this._comparedOrder[userId] : {};
        const comparedOrderLength = Object.keys(this._comparedOrder[userId]).length;
        sortable.forEach((question, index) => {
            this._comparedOrder[userId][question.question.questionId] = this._comparedOrder[userId][question.question.questionId] !== undefined ? this._comparedOrder[userId][question.question.questionId] : index + comparedOrderLength;
        });
    }

}

export default new QuestionStore();
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var createTestProperties = function createTestProperties() {
  if ( ! foam.lookup('test.PropTypeTester', true) ) {
    foam.CLASS({
      name: 'PropTypeTester',
      package: 'test',

      properties: [
        {
          class: 'Date',
          name: 'date',
        },
        {
          class: 'DateTime',
          name: 'dateTime',
        },
        {
          class: 'Long',
          name: 'long',
        },
        {
          class: 'Float',
          name: 'float',
        },
        {
          class: 'Function',
          name: 'func',
        },
        {
          class: 'Blob',
          name: 'blob',
        },
        {
          class: 'StringArray',
          name: 'stringArray',
        },
        {
          class: 'Class',
          name: 'class',
        }
        // TODO: other types, as they gain testable functionality
      ]
    });
  }

  return test.PropTypeTester.create(undefined, foam.__context__);
}
var createDateTestProperties = function createDateTestProperties() {
  if ( ! foam.lookup('test.DateTypeTester', true) ) {
    foam.CLASS({
      name: 'DateTypeTester',
      package: 'test',

      properties: [
        {
          class: 'Date',
          name: 'date',
        },
        {
          class: 'DateTime',
          name: 'dateTime',
        },
      ]
    });
  }
  return test.DateTypeTester.create(undefined, foam.__context__);
}

describe('Date', function() {
  var p;
  var p2;

  beforeEach(function() {
    p = createDateTestProperties();
    p2 = createDateTestProperties();
  });
  afterEach(function() {
    p = null;
    p2 = null;
  });

  it('accepts dates', function() {
    var d = new Date();
    p.date = d;
    expect(p.date).toBe(d);
  });
  it('accepts numbers', function() {
    p.date = 3434;
    expect(p.date).toEqual(new Date(3434));
  });
  it('accepts valid date strings', function() {
    dateStr = "Wed Dec 31 1987 19:00:03 GMT-0500 (EST)";
    p.date = dateStr;
    expect(p.date).toEqual(new Date(dateStr));
  });
  it('accepts invalid date strings, retains invalidity', function() {
    dateStr = "d ";
    p.date = dateStr;
    expect(p.date.toUTCString()).toEqual((new Date(dateStr)).toUTCString());
  });
  it('compares', function() {
    p.date = 55555;
    p2.date = 88888;

    expect(p.compareTo(p2)).toEqual(-1);
    expect(p2.compareTo(p)).toEqual(1);
  });
  it('compares when undefined', function() {
    p2.date = 88888;

    expect(p.compareTo(p2)).toEqual(-1);
    expect(p2.compareTo(p)).toEqual(1);
  });
  it('compares when undefined2', function() {
    p.date = 55555;

    expect(p.compareTo(p2)).toEqual(1);
    expect(p2.compareTo(p)).toEqual(-1);
  });
  it('compares when equal', function() {
    expect(p.compareTo(p2)).toEqual(0);
    expect(p2.compareTo(p)).toEqual(0);

    p.date =  55555;
    p2.date = 55555;
    expect(p.compareTo(p2)).toEqual(0);
    expect(p2.compareTo(p)).toEqual(0);
    expect(p.equals(p2)).toEqual(true);

  });
});

describe('Float', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('accepts ints', function() {
    p.float = 5;
    expect(p.float).toEqual(5);
  });
  it('accepts floats', function() {
    p.float = 456.456332;
    expect(p.float).toEqual(456.456332);
  });
  it('accepts negative floats', function() {
    p.float = -3.948474;
    expect(p.float).toEqual(-3.948474);
  });
  it('accepts string floats', function() {
    p.float = "-3.948474";
    expect(p.float).toEqual(-3.948474);
  });
  it('defaults to 0 if falsey', function() {
    p.float = null;
    expect(p.float).toEqual(0);
    p.float = '';
    expect(p.float).toEqual(0);
    p.float = undefined;
    expect(p.float).toEqual(0);
  });
});

describe('Function', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('defaults to a function', function() {
    expect(typeof p.func).toEqual('function');
    p.func();
  });
  it('accepts functions', function() {
    p.func = function() { return 55; }
    expect(p.func()).toEqual(55);
  });
  it('rejects non functions', function() {
    expect(function() {
      p.func = 'asdlfkjalskdjf';
    }).toThrow();
  });
});

describe('StringArray', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('setting an array to a number throws an exception', function() {
    expect(function() { p.stringArray = 42; }).toThrow();
  });
  it('setting an array to null throws an exception', function() {
    expect(function() { p.stringArray = null; }).toThrow();
  });
  it('setting an array to an object throws an exception', function() {
    expect(function() { p.stringArray = {}; }).toThrow();
  });
  it('is empty array by default', function() {
    expect(p.stringArray).toEqual([]);
  });
  it('accepts string in an array', function() {
    p.stringArray = [ "Hello", "I see", "Well" ];
    expect(p.stringArray).toEqual([ "Hello", "I see", "Well" ]);
  });
  it('converts elements to strings', function() {
    var d = new Date();
    var golden = '' + d;

    p.stringArray = [
      { a: 1 },
      2,
      true,
      d,
      'hello'
    ];

    expect(p.stringArray).toEqual([
      '[object Object]',
      '2',
      'true',
      golden,
      'hello'
    ]);
  });
});

describe('Class property', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
    createDateTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('is undefined by default', function() {
    expect(p.class).toBeUndefined();
  });

  it('stores a given model instance', function() {
    p.class = test.DateTypeTester;
    expect(p.class).toBe(test.DateTypeTester);
  });
  it('looks up a model from a string name', function() {
    p.class = 'test.DateTypeTester';
    expect(p.class).toBe(test.DateTypeTester);
  });
  it('accepts undefined', function() {
    p.class = 'test.DateTypeTester';
    expect(p.class).toBe(test.DateTypeTester);

    p.class = undefined;
    expect(p.class).toBeUndefined();
  });

});
<commit_msg>Fix StringArray tests.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var createTestProperties = function createTestProperties() {
  if ( ! foam.lookup('test.PropTypeTester', true) ) {
    foam.CLASS({
      name: 'PropTypeTester',
      package: 'test',

      properties: [
        {
          class: 'Date',
          name: 'date',
        },
        {
          class: 'DateTime',
          name: 'dateTime',
        },
        {
          class: 'Long',
          name: 'long',
        },
        {
          class: 'Float',
          name: 'float',
        },
        {
          class: 'Function',
          name: 'func',
        },
        {
          class: 'Blob',
          name: 'blob',
        },
        {
          class: 'StringArray',
          name: 'stringArray',
        },
        {
          class: 'Class',
          name: 'class',
        }
        // TODO: other types, as they gain testable functionality
      ]
    });
  }

  return test.PropTypeTester.create(undefined, foam.__context__);
}
var createDateTestProperties = function createDateTestProperties() {
  if ( ! foam.lookup('test.DateTypeTester', true) ) {
    foam.CLASS({
      name: 'DateTypeTester',
      package: 'test',

      properties: [
        {
          class: 'Date',
          name: 'date',
        },
        {
          class: 'DateTime',
          name: 'dateTime',
        },
      ]
    });
  }
  return test.DateTypeTester.create(undefined, foam.__context__);
}

describe('Date', function() {
  var p;
  var p2;

  beforeEach(function() {
    p = createDateTestProperties();
    p2 = createDateTestProperties();
  });
  afterEach(function() {
    p = null;
    p2 = null;
  });

  it('accepts dates', function() {
    var d = new Date();
    p.date = d;
    expect(p.date).toBe(d);
  });
  it('accepts numbers', function() {
    p.date = 3434;
    expect(p.date).toEqual(new Date(3434));
  });
  it('accepts valid date strings', function() {
    dateStr = "Wed Dec 31 1987 19:00:03 GMT-0500 (EST)";
    p.date = dateStr;
    expect(p.date).toEqual(new Date(dateStr));
  });
  it('accepts invalid date strings, retains invalidity', function() {
    dateStr = "d ";
    p.date = dateStr;
    expect(p.date.toUTCString()).toEqual((new Date(dateStr)).toUTCString());
  });
  it('compares', function() {
    p.date = 55555;
    p2.date = 88888;

    expect(p.compareTo(p2)).toEqual(-1);
    expect(p2.compareTo(p)).toEqual(1);
  });
  it('compares when undefined', function() {
    p2.date = 88888;

    expect(p.compareTo(p2)).toEqual(-1);
    expect(p2.compareTo(p)).toEqual(1);
  });
  it('compares when undefined2', function() {
    p.date = 55555;

    expect(p.compareTo(p2)).toEqual(1);
    expect(p2.compareTo(p)).toEqual(-1);
  });
  it('compares when equal', function() {
    expect(p.compareTo(p2)).toEqual(0);
    expect(p2.compareTo(p)).toEqual(0);

    p.date =  55555;
    p2.date = 55555;
    expect(p.compareTo(p2)).toEqual(0);
    expect(p2.compareTo(p)).toEqual(0);
    expect(p.equals(p2)).toEqual(true);

  });
});

describe('Float', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('accepts ints', function() {
    p.float = 5;
    expect(p.float).toEqual(5);
  });
  it('accepts floats', function() {
    p.float = 456.456332;
    expect(p.float).toEqual(456.456332);
  });
  it('accepts negative floats', function() {
    p.float = -3.948474;
    expect(p.float).toEqual(-3.948474);
  });
  it('accepts string floats', function() {
    p.float = "-3.948474";
    expect(p.float).toEqual(-3.948474);
  });
  it('defaults to 0 if falsey', function() {
    p.float = null;
    expect(p.float).toEqual(0);
    p.float = '';
    expect(p.float).toEqual(0);
    p.float = undefined;
    expect(p.float).toEqual(0);
  });
});

describe('Function', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('defaults to a function', function() {
    expect(typeof p.func).toEqual('function');
    p.func();
  });
  it('accepts functions', function() {
    p.func = function() { return 55; }
    expect(p.func()).toEqual(55);
  });
  it('rejects non functions', function() {
    expect(function() {
      p.func = 'asdlfkjalskdjf';
    }).toThrow();
  });
});

describe('StringArray', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('setting an array to a number throws an exception', function() {
    expect(function() { p.stringArray = 42; }).toThrow();
  });
  it('setting an array to null is permitted', function() {
    p.stringArray = null;
    expect(p.stringArray).toBe(null);
  });
  it('setting an array to an object throws an exception', function() {
    expect(function() { p.stringArray = {}; }).toThrow();
  });
  it('is empty array by default', function() {
    expect(p.stringArray).toEqual([]);
  });
  it('accepts string in an array', function() {
    p.stringArray = [ "Hello", "I see", "Well" ];
    expect(p.stringArray).toEqual([ "Hello", "I see", "Well" ]);
  });
  it('converts elements to strings', function() {
    var d = new Date();
    var golden = '' + d;

    p.stringArray = [
      { a: 1 },
      2,
      true,
      d,
      'hello'
    ];

    expect(p.stringArray).toEqual([
      '[object Object]',
      '2',
      'true',
      golden,
      'hello'
    ]);
  });
});

describe('Class property', function() {
  var p;

  beforeEach(function() {
    p = createTestProperties();
    createDateTestProperties();
  });
  afterEach(function() {
    p = null;
  });

  it('is undefined by default', function() {
    expect(p.class).toBeUndefined();
  });

  it('stores a given model instance', function() {
    p.class = test.DateTypeTester;
    expect(p.class).toBe(test.DateTypeTester);
  });
  it('looks up a model from a string name', function() {
    p.class = 'test.DateTypeTester';
    expect(p.class).toBe(test.DateTypeTester);
  });
  it('accepts undefined', function() {
    p.class = 'test.DateTypeTester';
    expect(p.class).toBe(test.DateTypeTester);

    p.class = undefined;
    expect(p.class).toBeUndefined();
  });

});
<|endoftext|>


<commit_before>module.exports = function(grunt) {
  "use strict";
  grunt.loadNpmTasks("grunt-contrib-requirejs");

  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    requirejs: {
      compile: {
        options: {
          baseUrl: "js/",
          mainConfigFile: "static/js/main.js",
          appDir: "static/",
          dir: "static-built/",
          skipDirOptimize: true,
          generateSourceMaps: false,
          findNestedDependencies: true,
          preserveLicenseComments: false,
          removeCombined: true,
          uglify2: {
            mangle: false
          },
          modules: [
            {
              name: "main",
              exclude: ["vendor-angular", "vendor-jquery", "vendor-misc"]
            },
            {
              name: "vendor-angular"
            },
            {
              name: "vendor-jquery"
            },
            {
              name: "vendor-misc"
            }
          ],
          optimize: "uglify2",
          optimizeCss: "none",
          wrapShim: 'true'
        }
      }
    }
  });
};
<commit_msg>Enable sourcemaps<commit_after>module.exports = function(grunt) {
  "use strict";
  grunt.loadNpmTasks("grunt-contrib-requirejs");

  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    requirejs: {
      compile: {
        options: {
          baseUrl: "js/",
          mainConfigFile: "static/js/main.js",
          appDir: "static/",
          dir: "static-built/",
          skipDirOptimize: true,
          generateSourceMaps: true,
          findNestedDependencies: true,
          preserveLicenseComments: false,
          removeCombined: true,
          uglify2: {
            mangle: false
          },
          modules: [
            {
              name: "main",
              exclude: ["vendor-angular", "vendor-jquery", "vendor-misc"]
            },
            {
              name: "vendor-angular"
            },
            {
              name: "vendor-jquery"
            },
            {
              name: "vendor-misc"
            }
          ],
          optimize: "uglify2",
          optimizeCss: "none",
          useSourceUrl: true,
          wrapShim: 'true'
        }
      }
    }
  });
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {

	var exec = require('child_process').exec,
		handlebars = require('handlebars'),
		moment = require('moment'),
		pkg = grunt.file.readJSON('package.json'),
		bower_pkg = grunt.file.readJSON('bower.json'),
		semver = require('semver'),
		path = require('path');

	// TODO: Remove Handlebars dependency and use the built-in grunt templating
	// Handlebars helpers
	handlebars.registerHelper('if', function(conditional, options) {
		if (options.hash.desired === options.hash.test) {
			return options.fn(this);
		}
	});

	// Project config
	grunt.initConfig({
		pkg: pkg,
		clean: {
			'docs': {
				options: { force: true },
				src: ['./docs/dist']
			},
			'github-pages': {
				options: { force: true },
				src: ['../gh-pages/src']
			},
			'F2-examples': {
				options: { force: true },
				src: ['./F2-examples.zip']
			}
		},
		copy: {
			'docs': {
				files: [
					{
						expand: true,
						flatten: true,
						src: [
							'sdk/*.js',
							'sdk/*.map'
						],
						dest: 'docs/dist/js/'
					},
					{
						cwd: 'docs/src',
						expand: true,
						src: [
							'img/**/*',
							'fonts/**/*',
							'apps/**/*',
							'js/**/*'
						],
						dest: 'docs/dist/'
					},
				]
			},
			'f2ToRoot': {
				files: [
					{
						expand: true,
						cwd: 'sdk/',
						src: 'f2.min.js',
						dest: './',
						rename: function(dest,src){
							return './<%= pkg.name %>.latest.js';
						}
					}
				]
			},
			'f2Dist': {
				files: [
					{
						expand: true,
						flatten: true,
						src: [
							'sdk/packages/*',
							'sdk/*.js',
							'sdk/*.map'
						],
						dest: 'dist/',
						filter: 'isFile'
					}
				]
			},
			'github-pages': {
				files: [
					{
						expand: true,
						cwd: 'docs/dist',
						src: ['**'],
						dest: '../gh-pages'
					},
					{
						expand: true,
						cwd: './',
						src: ['f2.latest.js'],
						rename: function(dest,src){
							return '../gh-pages/js/f2.min.js';//See #35
						}
					}
				]
			},
			'F2-examples': {
				files: [
					{
						expand: true,
						cwd: './',
						src: ['F2-examples.zip'],
						dest: '../gh-pages'
					}
				]
			}
		},
		compress: {
			main: {
				options: {
					archive: 'F2-examples.zip',
					pretty: true
				},
				files: [
					{
						expand: true,
						cwd: 'examples/',
						src: ['**'],
						dest: 'examples/'
					},
					{
						expand: true,
						cwd: 'sdk/',
						src: ['f2.debug.js'],
						dest: 'sdk/'
					},
					{
						expand: true,
						cwd: 'sdk/',
						src: ['src/third-party/require.min.js'],
						dest: 'sdk/'
					}
				]
			}
		},
		concat: {
			options: {
				process: { data: pkg },
				separator: '\n',
				stripBanners: false
			},
			dist: {
				src: [
					'sdk/src/template/header.js.tmpl',
					'sdk/src/third-party/domify.js',
					'sdk/src/third-party/fetch-jsonp-shim-start.js',
					'sdk/src/third-party/fetch-jsonp.js',
					'sdk/src/third-party/fetch-jsonp-shim-end.js',
					'sdk/src/third-party/lodash.custom.js',
					'sdk/src/third-party/jquery.js',
					'sdk/src/third-party/noconflict.js',
					'sdk/src/third-party/eventemitter2.js',
					'<%= jshint.files %>',
					'sdk/src/template/footer.js.tmpl'
				],
				dest: 'sdk/f2.debug.js'
			}
		},
		express: {
			docs: {
				options: {
					bases: [path.resolve('./docs/dist')]
				}
			},
			server: {
				options: {
					bases: './',
					port: 8080,
					server: path.resolve('./tests/server')
				}
			},
			// this is a duplicate of the above ^^^ to allow for testing cross origin
			// requests (GET/JSONP vs POST/JSON)
			server2: {
				options: {
					bases: './',
					port: 8081,
					server: path.resolve('./tests/server')
				}
			}
		},
		jasmine: {
			'non-amd': {
				options: {
					host: 'http://localhost:8080/tests/',
					outfile: 'index.html'
				}
			},
			'amd': {
				options: {
					host: 'http://localhost:8080/tests/',
					outfile: 'index-amd.html'
				}
			}
		},
		jshint: {
			options: {
				jshintrc: true
			},
			files: [
				'sdk/src/F2.js',
				'sdk/src/app_handlers.js',
				'sdk/src/classes.js',
				'sdk/src/constants.js',
				'sdk/src/events.js',
				'sdk/src/rpc.js',
				'sdk/src/ui.js',
				'sdk/src/container.js'
			]
		},
		less: {
			production: {
				options: {
					paths: [
						'docs/src/css/less',
					],
					modifyVars: {
						imgPath: '',
						version: '<%= pkg.version %>'
					},
					compress: true,
					banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> Copyright Markit On Demand, Inc. */\n'
				},
				files: {
					'docs/dist/css/site.css': 'docs/src/css/less/site.less'
				}
			}
		},
		uglify: {
			options: {
				preserveComments: 'some',
				banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("mm-dd-yyyy") %> - See below for copyright and license */\n'
			},
			dist: {
				files: {'sdk/f2.min.js' : ['sdk/f2.debug.js']},
				options: {
					report: 'min'
				}
			},
			sourcemap: {
				files: '<%= uglify.dist.files %>',
				options: {
					sourceMap: function(fileName) {
						return fileName.replace(/\.js$/, '.map');
					},
					sourceMapPrefix: 1,
					sourceMappingURL: function(path) {
						return path.replace(grunt.config('sourcemap.options.prefix'), '').replace(/\.js$/, '.map');
					}
				}
			}
		},
		sourcemap: {
			options: {
				src: 'sdk/f2.min.js',
				prefix: 'sdk/'
			}
		},
		watch: {
			docs: {
				files: ['docs/src/**/*.*','package.json','docs/bin/gen-docs.js'],
				tasks: ['docs'],
				options: {
					spawn: false
				}
			},
			scripts: {
				files: ['./sdk/src/**/*.js', '!./sdk/src/third-party/**/*.js'],
				tasks: ['js'],
				options: {
					spawn: false
				}
			}
		}
	});

	// Register tasks
	grunt.registerTask('fix-sourcemap', 'Fixes the source map file', function() {
		var uglifyOptions = grunt.config('uglify.sourcemap.options'),
			options = grunt.config('sourcemap.options'),
			dest = uglifyOptions.sourceMap(options.src),
			rawMap = grunt.file.read(dest);

		rawMap = rawMap.replace(options.prefix, '');
		grunt.file.write(dest, rawMap);
	});

	grunt.registerTask('generate-docs', 'Generate docs', function() {
		var done = this.async(),
			log = grunt.log.write('Generating docs...');

		exec('node ' + path.join(__dirname, 'docs/bin/gen-docs'), function(err, stdout, stderr) {
			if (err) {
				grunt.log.error(err.message);
				grunt.fail.fatal('Docs generation aborted.');
				return;
			}
			grunt.log.write(stdout);
			log.ok();
			done();
		});
	});

	grunt.registerTask('nuget', 'Builds the NuGet package for distribution on NuGet.org', function() {
		var done = this.async(),
			log = grunt.log.write('Creating NuSpec file...'),
			nuspec = grunt.file.read('./sdk/f2.nuspec.tmpl');

		nuspec = grunt.template.process(nuspec, { data: pkg });
		grunt.file.write('./sdk/f2.nuspec', nuspec);
		log.ok();

		log = grunt.log.write('Creating NuGet package...');
		grunt.util.spawn(
			{
				cmd: 'nuget',
				args: ['pack', 'f2.nuspec'],
				opts: {
					cwd: './sdk'
				}
			},
			function(error, result, code){
				if (error){
					grunt.fail.fatal(error);
				} else {
					grunt.file.delete('./sdk/f2.nuspec');
					log.ok();
					done();
				}
			}
		);
	});

	grunt.registerTask('release', 'Prepares the code for release (merge into master)', function(releaseType) {
		if (!/^major|minor|patch$/i.test(releaseType) && !semver.valid(releaseType)) {
			grunt.log.error('"' + releaseType + '" is not a valid release type (major, minor, or patch) or SemVer version');
			return;
		}

		var version = semver.valid(releaseType) ? releaseType : String(semver.inc(pkg.version, releaseType)).replace(/\-\w+$/, '');

		pkg.version = version;
		bower_pkg.version = version;

		pkg._releaseDate = new Date().toJSON();
		pkg._releaseDateFormatted = moment(pkg._releaseDate).format('D MMMM YYYY');

		grunt.file.write('./package.json', JSON.stringify(pkg, null, '\t'));
		grunt.file.write('./bower.json', JSON.stringify(bower_pkg, null, '\t'));
		grunt.config.set('pkg', pkg);

		grunt.task.run('version');
	});

	grunt.registerTask('version', 'Displays version information for F2', function() {
		grunt.log.writeln(grunt.template.process(
			'This copy of F2 is at version <%= version %> with a release date of <%= _releaseDateFormatted %>',
			{ data: pkg }
		));
	});

	grunt.registerTask('yuidoc', 'Builds the reference docs with YUIDocJS', function() {
		var done = this.async();
		var log = grunt.log.write('Generating reference docs...');

		// using YUIDoc within a grunt task OR using grunt-contrib-yuidoc results in a
		// this.parsedir error. See https://github.com/gruntjs/grunt-contrib-yuidoc/issues/33
		// the grunt-contrib-yuidoc folks blame it on yuidoc and the yuidoc folks blame it
		// on grunt-contrib-yuidoc (https://github.com/yui/yuidoc/issues/242)
		// it seems like its actually a grunt issue so for now using an external script seems
		// to work fine
		exec('node ' + path.join(__dirname, 'docs/bin/yuidocs.js'), function(err, stdout, stderr) {
			if (err) {
				grunt.log.error(err.message);
				grunt.fail.fatal('YUIDocs failed.');
				return;
			}
			grunt.log.error(stderr);
			grunt.log.write(stdout);
			log.ok();
			done();
		});
	});

	// Load plugins
	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-copy');
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-compress');
	grunt.loadNpmTasks('grunt-contrib-jasmine');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-express');

	grunt.registerTask('docs', ['clean:docs','less','generate-docs', 'yuidoc', 'copy:docs']);
	grunt.registerTask('docs-dev', ['docs', 'express:docs', 'watch:docs'/*,'express-keepalive'*/]);
	grunt.registerTask('github-pages', ['copy:github-pages', 'clean:github-pages']);
	grunt.registerTask('zip', ['compress', 'copy:F2-examples', 'clean:F2-examples']);
	grunt.registerTask('js', ['concat:dist', 'uglify:dist', 'sourcemap', 'copy:f2ToRoot', 'copy:f2Dist', 'copy:f2ToDocs']);
	grunt.registerTask('sourcemap', ['uglify:sourcemap']);
	grunt.registerTask('test', ['jshint', 'express', 'jasmine']);
	grunt.registerTask('test-live', ['jshint', 'express', 'express-keepalive']);
	grunt.registerTask('travis', ['test']);
	grunt.registerTask('default', ['test', 'js', 'docs', 'zip']);
	grunt.registerTask('build', ['js', 'docs', 'zip', 'nuget'])
};
<commit_msg>feat: add sourcemaps for css<commit_after>module.exports = function(grunt) {

	var exec = require('child_process').exec,
		handlebars = require('handlebars'),
		moment = require('moment'),
		pkg = grunt.file.readJSON('package.json'),
		bower_pkg = grunt.file.readJSON('bower.json'),
		semver = require('semver'),
		path = require('path');

	// TODO: Remove Handlebars dependency and use the built-in grunt templating
	// Handlebars helpers
	handlebars.registerHelper('if', function(conditional, options) {
		if (options.hash.desired === options.hash.test) {
			return options.fn(this);
		}
	});

	// Project config
	grunt.initConfig({
		pkg: pkg,
		clean: {
			'docs': {
				options: { force: true },
				src: ['./docs/dist']
			},
			'github-pages': {
				options: { force: true },
				src: ['../gh-pages/src']
			},
			'F2-examples': {
				options: { force: true },
				src: ['./F2-examples.zip']
			}
		},
		copy: {
			'docs': {
				files: [
					{
						expand: true,
						flatten: true,
						src: [
							'sdk/*.js',
							'sdk/*.map'
						],
						dest: 'docs/dist/js/'
					},
					{
						cwd: 'docs/src',
						expand: true,
						src: [
							'img/**/*',
							'fonts/**/*',
							'apps/**/*',
							'js/**/*'
						],
						dest: 'docs/dist/'
					},
				]
			},
			'f2ToRoot': {
				files: [
					{
						expand: true,
						cwd: 'sdk/',
						src: 'f2.min.js',
						dest: './',
						rename: function(dest,src){
							return './<%= pkg.name %>.latest.js';
						}
					}
				]
			},
			'f2Dist': {
				files: [
					{
						expand: true,
						flatten: true,
						src: [
							'sdk/packages/*',
							'sdk/*.js',
							'sdk/*.map'
						],
						dest: 'dist/',
						filter: 'isFile'
					}
				]
			},
			'github-pages': {
				files: [
					{
						expand: true,
						cwd: 'docs/dist',
						src: ['**'],
						dest: '../gh-pages'
					},
					{
						expand: true,
						cwd: './',
						src: ['f2.latest.js'],
						rename: function(dest,src){
							return '../gh-pages/js/f2.min.js';//See #35
						}
					}
				]
			},
			'F2-examples': {
				files: [
					{
						expand: true,
						cwd: './',
						src: ['F2-examples.zip'],
						dest: '../gh-pages'
					}
				]
			}
		},
		compress: {
			main: {
				options: {
					archive: 'F2-examples.zip',
					pretty: true
				},
				files: [
					{
						expand: true,
						cwd: 'examples/',
						src: ['**'],
						dest: 'examples/'
					},
					{
						expand: true,
						cwd: 'sdk/',
						src: ['f2.debug.js'],
						dest: 'sdk/'
					},
					{
						expand: true,
						cwd: 'sdk/',
						src: ['src/third-party/require.min.js'],
						dest: 'sdk/'
					}
				]
			}
		},
		concat: {
			options: {
				process: { data: pkg },
				separator: '\n',
				stripBanners: false
			},
			dist: {
				src: [
					'sdk/src/template/header.js.tmpl',
					'sdk/src/third-party/domify.js',
					'sdk/src/third-party/fetch-jsonp-shim-start.js',
					'sdk/src/third-party/fetch-jsonp.js',
					'sdk/src/third-party/fetch-jsonp-shim-end.js',
					'sdk/src/third-party/lodash.custom.js',
					'sdk/src/third-party/jquery.js',
					'sdk/src/third-party/noconflict.js',
					'sdk/src/third-party/eventemitter2.js',
					'<%= jshint.files %>',
					'sdk/src/template/footer.js.tmpl'
				],
				dest: 'sdk/f2.debug.js'
			}
		},
		express: {
			docs: {
				options: {
					bases: [path.resolve('./docs/dist')]
				}
			},
			server: {
				options: {
					bases: './',
					port: 8080,
					server: path.resolve('./tests/server')
				}
			},
			// this is a duplicate of the above ^^^ to allow for testing cross origin
			// requests (GET/JSONP vs POST/JSON)
			server2: {
				options: {
					bases: './',
					port: 8081,
					server: path.resolve('./tests/server')
				}
			}
		},
		jasmine: {
			'non-amd': {
				options: {
					host: 'http://localhost:8080/tests/',
					outfile: 'index.html'
				}
			},
			'amd': {
				options: {
					host: 'http://localhost:8080/tests/',
					outfile: 'index-amd.html'
				}
			}
		},
		jshint: {
			options: {
				jshintrc: true
			},
			files: [
				'sdk/src/F2.js',
				'sdk/src/app_handlers.js',
				'sdk/src/classes.js',
				'sdk/src/constants.js',
				'sdk/src/events.js',
				'sdk/src/rpc.js',
				'sdk/src/ui.js',
				'sdk/src/container.js'
			]
		},
		less: {
			production: {
				options: {
					paths: [
						'docs/src/css/less',
					],
					modifyVars: {
						imgPath: '',
						version: '<%= pkg.version %>'
					},
					compress: true,
					sourceMap: true,
					sourceMapURL: '/css/site.css.map',
					banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> Copyright Markit On Demand, Inc. */\n'
				},
				files: {
					'docs/dist/css/site.css': 'docs/src/css/less/site.less'
				}
			}
		},
		uglify: {
			options: {
				preserveComments: 'some',
				banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("mm-dd-yyyy") %> - See below for copyright and license */\n'
			},
			dist: {
				files: {'sdk/f2.min.js' : ['sdk/f2.debug.js']},
				options: {
					report: 'min'
				}
			},
			sourcemap: {
				files: '<%= uglify.dist.files %>',
				options: {
					sourceMap: function(fileName) {
						return fileName.replace(/\.js$/, '.map');
					},
					sourceMapPrefix: 1,
					sourceMappingURL: function(path) {
						return path.replace(grunt.config('sourcemap.options.prefix'), '').replace(/\.js$/, '.map');
					}
				}
			}
		},
		sourcemap: {
			options: {
				src: 'sdk/f2.min.js',
				prefix: 'sdk/'
			}
		},
		watch: {
			docs: {
				files: ['docs/src/**/*.*','package.json','docs/bin/gen-docs.js'],
				tasks: ['docs'],
				options: {
					spawn: false
				}
			},
			scripts: {
				files: ['./sdk/src/**/*.js', '!./sdk/src/third-party/**/*.js'],
				tasks: ['js'],
				options: {
					spawn: false
				}
			}
		}
	});

	// Register tasks
	grunt.registerTask('fix-sourcemap', 'Fixes the source map file', function() {
		var uglifyOptions = grunt.config('uglify.sourcemap.options'),
			options = grunt.config('sourcemap.options'),
			dest = uglifyOptions.sourceMap(options.src),
			rawMap = grunt.file.read(dest);

		rawMap = rawMap.replace(options.prefix, '');
		grunt.file.write(dest, rawMap);
	});

	grunt.registerTask('generate-docs', 'Generate docs', function() {
		var done = this.async(),
			log = grunt.log.write('Generating docs...');

		exec('node ' + path.join(__dirname, 'docs/bin/gen-docs'), function(err, stdout, stderr) {
			if (err) {
				grunt.log.error(err.message);
				grunt.fail.fatal('Docs generation aborted.');
				return;
			}
			grunt.log.write(stdout);
			log.ok();
			done();
		});
	});

	grunt.registerTask('nuget', 'Builds the NuGet package for distribution on NuGet.org', function() {
		var done = this.async(),
			log = grunt.log.write('Creating NuSpec file...'),
			nuspec = grunt.file.read('./sdk/f2.nuspec.tmpl');

		nuspec = grunt.template.process(nuspec, { data: pkg });
		grunt.file.write('./sdk/f2.nuspec', nuspec);
		log.ok();

		log = grunt.log.write('Creating NuGet package...');
		grunt.util.spawn(
			{
				cmd: 'nuget',
				args: ['pack', 'f2.nuspec'],
				opts: {
					cwd: './sdk'
				}
			},
			function(error, result, code){
				if (error){
					grunt.fail.fatal(error);
				} else {
					grunt.file.delete('./sdk/f2.nuspec');
					log.ok();
					done();
				}
			}
		);
	});

	grunt.registerTask('release', 'Prepares the code for release (merge into master)', function(releaseType) {
		if (!/^major|minor|patch$/i.test(releaseType) && !semver.valid(releaseType)) {
			grunt.log.error('"' + releaseType + '" is not a valid release type (major, minor, or patch) or SemVer version');
			return;
		}

		var version = semver.valid(releaseType) ? releaseType : String(semver.inc(pkg.version, releaseType)).replace(/\-\w+$/, '');

		pkg.version = version;
		bower_pkg.version = version;

		pkg._releaseDate = new Date().toJSON();
		pkg._releaseDateFormatted = moment(pkg._releaseDate).format('D MMMM YYYY');

		grunt.file.write('./package.json', JSON.stringify(pkg, null, '\t'));
		grunt.file.write('./bower.json', JSON.stringify(bower_pkg, null, '\t'));
		grunt.config.set('pkg', pkg);

		grunt.task.run('version');
	});

	grunt.registerTask('version', 'Displays version information for F2', function() {
		grunt.log.writeln(grunt.template.process(
			'This copy of F2 is at version <%= version %> with a release date of <%= _releaseDateFormatted %>',
			{ data: pkg }
		));
	});

	grunt.registerTask('yuidoc', 'Builds the reference docs with YUIDocJS', function() {
		var done = this.async();
		var log = grunt.log.write('Generating reference docs...');

		// using YUIDoc within a grunt task OR using grunt-contrib-yuidoc results in a
		// this.parsedir error. See https://github.com/gruntjs/grunt-contrib-yuidoc/issues/33
		// the grunt-contrib-yuidoc folks blame it on yuidoc and the yuidoc folks blame it
		// on grunt-contrib-yuidoc (https://github.com/yui/yuidoc/issues/242)
		// it seems like its actually a grunt issue so for now using an external script seems
		// to work fine
		exec('node ' + path.join(__dirname, 'docs/bin/yuidocs.js'), function(err, stdout, stderr) {
			if (err) {
				grunt.log.error(err.message);
				grunt.fail.fatal('YUIDocs failed.');
				return;
			}
			grunt.log.error(stderr);
			grunt.log.write(stdout);
			log.ok();
			done();
		});
	});

	// Load plugins
	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-copy');
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-compress');
	grunt.loadNpmTasks('grunt-contrib-jasmine');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-express');

	grunt.registerTask('docs', ['clean:docs','less','generate-docs', 'yuidoc', 'copy:docs']);
	grunt.registerTask('docs-dev', ['docs', 'express:docs', 'watch:docs'/*,'express-keepalive'*/]);
	grunt.registerTask('github-pages', ['copy:github-pages', 'clean:github-pages']);
	grunt.registerTask('zip', ['compress', 'copy:F2-examples', 'clean:F2-examples']);
	grunt.registerTask('js', ['concat:dist', 'uglify:dist', 'sourcemap', 'copy:f2ToRoot', 'copy:f2Dist', 'copy:f2ToDocs']);
	grunt.registerTask('sourcemap', ['uglify:sourcemap']);
	grunt.registerTask('test', ['jshint', 'express', 'jasmine']);
	grunt.registerTask('test-live', ['jshint', 'express', 'express-keepalive']);
	grunt.registerTask('travis', ['test']);
	grunt.registerTask('default', ['test', 'js', 'docs', 'zip']);
	grunt.registerTask('build', ['js', 'docs', 'zip', 'nuget'])
};
<|endoftext|>


<commit_before>(function(global) {

var LITERAL = '[a-zA-Z0-9-]',
    MOD_SEPARATOR = '_',
    ELEM_SEPARATOR = '__';

var BEMNaming = function (options) {
    options || (options = {});

    this._separators = {};
    this._separators.modSeparator = options.modSeparator || MOD_SEPARATOR;
    this._separators.elemSeparator = options.elemSeparator || ELEM_SEPARATOR;
    this._literal = options.literal || LITERAL;

    this._buildRegex();
};

BEMNaming.prototype.parse = function (str) {
    var executed = this._regex.exec(str),
        modKey = executed[2] || executed[5],
        modValue = executed[3] || executed[6];

    if (modKey && modValue === undefined) {
        modValue = true;
    }

    return {
        block: executed[1],
        elem: executed[4],
        modKey: modKey,
        modValue: modValue
    };
};

BEMNaming.prototype.stringify = function (obj) {
    var buf = [obj.block];

    if (obj.elem) {
        buf.push(this._separators.elemSeparator + obj.elem);
    }

    if (obj.modKey) {
        buf.push(this._separators.modSeparator + obj.modKey);

        if (obj.modValue) {
            buf.push(this._separators.modSeparator + obj.modValue);
        }
    }

    return buf.join('');
};

BEMNaming.prototype._buildRegex = function () {
    var word = this._literal + '+',
        block = '(' + word + ')',
        mod = '(?:' + this._separators.modSeparator + '(' + word + '))?',
        elem = '(?:' + this._separators.elemSeparator + '(' + word + '))?';

    this._regex = new RegExp(['^', block, mod, mod, elem, mod, mod, '$'].join(''));
};

var defineAsGlobal = true,
    originalNaming = new BEMNaming({
        modSeparator: MOD_SEPARATOR,
        elemSeparator: ELEM_SEPARATOR,
        literal: LITERAL
    }),
    naming = {
        BEMNaming: BEMNaming,
        parse: originalNaming.parse.bind(originalNaming),
        stringify: originalNaming.stringify.bind(originalNaming)
    };

if(typeof exports === 'object') {
    module.exports = naming;
    defineAsGlobal = false;
}

if(typeof modules === 'object') {
    modules.define('bem-naming', function(provide) {
        provide(naming);
    });
    defineAsGlobal = false;
}

if(typeof define === 'function') {
    define(function(require, exports, module) {
        module.exports = naming;
    });
    defineAsGlobal = false;
}

defineAsGlobal && (global.naming = naming);

})(this);
<commit_msg>naming -> bemNaming<commit_after>(function(global) {

var LITERAL = '[a-zA-Z0-9-]',
    MOD_SEPARATOR = '_',
    ELEM_SEPARATOR = '__';

var BEMNaming = function (options) {
    options || (options = {});

    this._separators = {};
    this._separators.modSeparator = options.modSeparator || MOD_SEPARATOR;
    this._separators.elemSeparator = options.elemSeparator || ELEM_SEPARATOR;
    this._literal = options.literal || LITERAL;

    this._buildRegex();
};

BEMNaming.prototype.parse = function (str) {
    var executed = this._regex.exec(str),
        modKey = executed[2] || executed[5],
        modValue = executed[3] || executed[6];

    if (modKey && modValue === undefined) {
        modValue = true;
    }

    return {
        block: executed[1],
        elem: executed[4],
        modKey: modKey,
        modValue: modValue
    };
};

BEMNaming.prototype.stringify = function (obj) {
    var buf = [obj.block];

    if (obj.elem) {
        buf.push(this._separators.elemSeparator + obj.elem);
    }

    if (obj.modKey) {
        buf.push(this._separators.modSeparator + obj.modKey);

        if (obj.modValue) {
            buf.push(this._separators.modSeparator + obj.modValue);
        }
    }

    return buf.join('');
};

BEMNaming.prototype._buildRegex = function () {
    var word = this._literal + '+',
        block = '(' + word + ')',
        mod = '(?:' + this._separators.modSeparator + '(' + word + '))?',
        elem = '(?:' + this._separators.elemSeparator + '(' + word + '))?';

    this._regex = new RegExp(['^', block, mod, mod, elem, mod, mod, '$'].join(''));
};

var defineAsGlobal = true,
    originalNaming = new BEMNaming({
        modSeparator: MOD_SEPARATOR,
        elemSeparator: ELEM_SEPARATOR,
        literal: LITERAL
    }),
    bemNaming = {
        BEMNaming: BEMNaming,
        parse: originalNaming.parse.bind(originalNaming),
        stringify: originalNaming.stringify.bind(originalNaming)
    };

if(typeof exports === 'object') {
    module.exports = bemNaming;
    defineAsGlobal = false;
}

if(typeof modules === 'object') {
    modules.define('bem-naming', function(provide) {
        provide(bemNaming);
    });
    defineAsGlobal = false;
}

if(typeof define === 'function') {
    define(function(require, exports, module) {
        module.exports = bemNaming;
    });
    defineAsGlobal = false;
}

defineAsGlobal && (global.bemNaming = bemNaming);

})(this);
<|endoftext|>