<commit_before>/*jslint node: true */
'use strict';

var pkg = require('./package.json');

//Using exclusion patterns slows down Grunt significantly
//instead of creating a set of patterns like '**/*.js' and '!**/node_modules/**'
//this method is used to create a set of inclusive patterns for all subdirectories
//skipping node_modules, bower_components, dist, and any .dirs
//This enables users to create any directory structure they desire.
var createFolderGlobs = function(fileTypePatterns) {
  fileTypePatterns = Array.isArray(fileTypePatterns) ? fileTypePatterns : [fileTypePatterns];
  var ignore = ['node_modules','bower_components','dist','temp'];
  var fs = require('fs');
  return fs.readdirSync(process.cwd())
          .map(function(file){
            if (ignore.indexOf(file) !== -1 ||
                file.indexOf('.') === 0 ||
                !fs.lstatSync(file).isDirectory()) {
              return null;
            } else {
              return fileTypePatterns.map(function(pattern) {
                return file + '/**/' + pattern;
              });
            }
          })
          .filter(function(patterns){
            return patterns;
          })
          .concat(fileTypePatterns);
};

module.exports = function (grunt) {

  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  // Project configuration.
  grunt.initConfig({
    connect: {
      main: {
        options: {
          port: 9001
        }
      }
    },
    watch: {
      main: {
        options: {
            livereload: true,
            livereloadOnError: false,
            spawn: false
        },
        files: [createFolderGlobs(['*.js','*.less','*.html']),'!_SpecRunner.html','!.grunt'],
        tasks: [] //all the tasks are run dynamically during the watch event handler
      }
    },
    jshint: {
      main: {
        options: {
            jshintrc: '.jshintrc'
        },
        src: createFolderGlobs('*.js')
      }
    },
    clean: {
      before:{
        src:['dist','temp']
      },
      after: {
        src:['temp']
      }
    },
    less: {
      production: {
        options: {
        },
        files: {
          'temp/app.css': 'app.less'
        }
      }
    },
    autoprefixer: {
      options: {
        browsers: ['ie >= 8', 'ff > 4', 'last 8 versions']
      },
      main: {
        src: 'temp/app.css'
      }
    },
    ngtemplates: {
      main: {
        options: {
            module: pkg.name,
            htmlmin:'<%= htmlmin.main.options %>'
        },
        src: [createFolderGlobs('*.html'),'!index.html','!_SpecRunner.html'],
        dest: 'temp/templates.js'
      }
    },
    copy: {
      main: {
        files: [
          {src: ['img/**'], dest: 'dist/'},
          {src: ['temp_data/**'], dest: 'dist/'},
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/fonts/'
          },
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/fonts/'
          },
          {
            src: ['locales/**'],
            dest: 'dist/'
          },
//           {src: ['bower_components/font-awesome/fonts/**'], dest: 'dist/',filter:'isFile',expand:true}
          //{src: ['bower_components/angular-ui-utils/ui-utils-ieshiv.min.js'], dest: 'dist/'},
          //{src: ['bower_components/select2/*.png','bower_components/select2/*.gif'], dest:'dist/css/',flatten:true,expand:true},
          //{src: ['bower_components/angular-mocks/angular-mocks.js'], dest: 'dist/'}
        ]
      }
    },
    dom_munger:{
      read: {
        options: {
          read:[
            {selector:'script[class="libnocompat"]',attribute:'src',writeto:'libnocompatjs'},
            {selector:'script[class="lib"]',attribute:'src',writeto:'libjs'},
            {selector:'script[class="app"]',attribute:'src',writeto:'appjs'},
            {selector:'link[rel="stylesheet"][data-concat!="false"]',attribute:'href',writeto:'appcss'}
          ]
        },
        src: 'index.html'
      },
      update: {
        options: {
          remove: ['script[data-remove!="false"]','link[data-remove!="false"]'],
          append: [
            {selector:'body',html:'<!--[if lte IE 8]><script src="libcompat-v1.0.10.min.js"></script><![endif]--><!--[if gte IE 9]><script src="libnocompat-v1.0.10.min.js"></script><![endif]--><!--[if !IE]><!--><script src="libnocompat-v1.0.10.min.js"></script><!--<![endif]-->'},
            {selector:'body',html:'<script src="lib-v1.0.10.min.js"></script>'},
            {selector:'body',html:'<script src="avConfig-v1.0.10.js"></script>'},
            {selector:'body',html:'<script src="app-v1.0.10.min.js"></script>'},
            {selector:'head',html:'<link rel="stylesheet" href="app-v1.0.10.full.min.css">'}
          ]
        },
        src:'index.html',
        dest: 'dist/index.html'
      }
    },
    cssmin: {
      main: {
        src:['temp/app.css','<%= dom_munger.data.appcss %>'],
        dest:'dist/app-v1.0.10.full.min.css'
      },
    },
    concat: {
      main: {
        files: {
          'temp/libcompat.js': [
             'vendor/jquery.compat/jquery-1.11.1.js',
             'vendor/json3/json-v3.3.2.js',
             'vendor/crypto/unsupportedBrowser.js'
          ],
          'temp/libnocompat.js': ['<%= dom_munger.data.libnocompatjs %>'],
          'temp/lib.js': ['<%= dom_munger.data.libjs %>'],
          'temp/app.js': ['<%= dom_munger.data.appjs %>','<%= ngtemplates.main.dest %>'],
          'dist/avConfig-v1.0.10.js': ['avConfig.js']
        }
      }
    },
    ngAnnotate: {
      main: {
        files: {
        'temp/app.js':['temp/app.js'],
        'temp/lib.js': ['temp/lib.js'],
        'temp/libnocompat.js': ['temp/libnocompat.js'],
        'temp/libcompat.js': ['temp/libcompat.js']
        }
      }
    },
    uglify: {
      main: {
        /*options:{
          mangle: false,
          compress: false,
          beautify: true
        },*/
        files: {
          'dist/app-v1.0.10.min.js': 'temp/app.js',
          'dist/lib-v1.0.10.min.js': 'temp/lib.js',
          'dist/libnocompat-v1.0.10.min.js': 'temp/libnocompat.js',
          'dist/libcompat-v1.0.10.min.js': 'temp/libcompat.js'
        }
      }
    },
    htmlmin: {
      main: {
        options: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          removeAttributeQuotes: false,
          removeComments: false,
          removeEmptyAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        },
        files: {
          'dist/index.html': 'dist/index.html'
        }
      }
    },
    imagemin: {
      main:{
        files: [{
          expand: true, cwd:'dist/',
          src:['**/{*.png,*.jpg}'],
          dest: 'dist/'
        }]
      }
    },
    karma: {
      options: {
        frameworks: ['jasmine'],
        files: [  //this files data is also updated in the watch handler, if updated change there too
          '<%= dom_munger.data.libnocompatjs %>',
          '<%= dom_munger.data.libjs %>',
          'avConfig.js',
          '<%= dom_munger.data.appjs %>',
          '<%= ngtemplates.main.dest %>',
          'bower_components/angular-mocks/angular-mocks.js',
          createFolderGlobs('*-spec.js')
        ],
        logLevel:'ERROR',
        reporters:['mocha'],
        autoWatch: false, //watching is handled by grunt-contrib-watch
        singleRun: true
      },
      all_tests: {
        browsers: ['PhantomJS','Chrome','Firefox']
      },
      during_watch: {
        browsers: ['PhantomJS']
      },
    },
    protractor: {
      options: {
        configFile: "node_modules/protractor/referenceConf.js", // Default config file
        keepAlive: true, // If false, the grunt process stops when the test fails.
        noColor: false, // If true, protractor will not use colors in its output.
        args: {
        // Arguments passed to the command
      }
    },
    //your_target: {   // Grunt requires at least one target to run so you can simply put 'all: {}' here too.
      all: {
      options: {
        configFile: "e2e.conf.js", // Target-specific config file
        args: {} // Target-specific arguments
      }
    },
  },

  });

  grunt.registerTask('build',['jshint','clean:before','less','autoprefixer','dom_munger','ngtemplates','cssmin','concat','ngAnnotate','uglify','copy','htmlmin','imagemin','clean:after']);
  grunt.registerTask('serve', ['dom_munger:read','jshint','connect', 'watch']);
  grunt.registerTask('test',['dom_munger:read','karma:all_tests']);

  grunt.event.on('watch', function(action, filepath) {
    //https://github.com/gruntjs/grunt-contrib-watch/issues/156

    var tasksToRun = [];

    if ((filepath.lastIndexOf('.json') !== -1 && filepath.lastIndexOf('.json') === filepath.length - 5) ||
        (filepath.lastIndexOf('.js') !== -1 && filepath.lastIndexOf('.js') === filepath.length - 3)) {

      //lint the changed js file
      grunt.config('jshint.main.src', filepath);
      tasksToRun.push('jshint');

      //find the appropriate unit t est for the changed file
      var spec = filepath;
      if (filepath.lastIndexOf('-spec.js') === -1 || filepath.lastIndexOf('-spec.js') !== filepath.length - 8) {
        spec = filepath.substring(0,filepath.length - 3) + '-spec.js';
      }

      //if the spec exists then lets run it
      if (grunt.file.exists(spec)) {
        var files = [].concat(grunt.config('dom_munger.data.libnocompatjs'));
        files.concat(grunt.config('dom_munger.data.libjs'));
        files.push('bower_components/angular-mocks/angular-mocks.js');
        files.push('avConfig.js');
        files.concat(grunt.config('dom_munger.data.appjs'));
        files.concat(grunt.config('ngtemplates.main.dest'));
        files.push(spec);
        grunt.config('karma.options.files', files);
        tasksToRun.push('karma:during_watch');
      }
    }

    //if index.html changed, we need to reread the <script> tags so our next run of karma
    //will have the correct environment
    if (filepath === 'index.html') {
      tasksToRun.push('dom_munger:read');
    }

    grunt.config('watch.main.tasks',tasksToRun);

  });

  grunt.loadNpmTasks('grunt-protractor-runner');
};
<commit_msg>bump version<commit_after>/*jslint node: true */
'use strict';

var pkg = require('./package.json');

//Using exclusion patterns slows down Grunt significantly
//instead of creating a set of patterns like '**/*.js' and '!**/node_modules/**'
//this method is used to create a set of inclusive patterns for all subdirectories
//skipping node_modules, bower_components, dist, and any .dirs
//This enables users to create any directory structure they desire.
var createFolderGlobs = function(fileTypePatterns) {
  fileTypePatterns = Array.isArray(fileTypePatterns) ? fileTypePatterns : [fileTypePatterns];
  var ignore = ['node_modules','bower_components','dist','temp'];
  var fs = require('fs');
  return fs.readdirSync(process.cwd())
          .map(function(file){
            if (ignore.indexOf(file) !== -1 ||
                file.indexOf('.') === 0 ||
                !fs.lstatSync(file).isDirectory()) {
              return null;
            } else {
              return fileTypePatterns.map(function(pattern) {
                return file + '/**/' + pattern;
              });
            }
          })
          .filter(function(patterns){
            return patterns;
          })
          .concat(fileTypePatterns);
};

module.exports = function (grunt) {

  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  // Project configuration.
  grunt.initConfig({
    connect: {
      main: {
        options: {
          port: 9001
        }
      }
    },
    watch: {
      main: {
        options: {
            livereload: true,
            livereloadOnError: false,
            spawn: false
        },
        files: [createFolderGlobs(['*.js','*.less','*.html']),'!_SpecRunner.html','!.grunt'],
        tasks: [] //all the tasks are run dynamically during the watch event handler
      }
    },
    jshint: {
      main: {
        options: {
            jshintrc: '.jshintrc'
        },
        src: createFolderGlobs('*.js')
      }
    },
    clean: {
      before:{
        src:['dist','temp']
      },
      after: {
        src:['temp']
      }
    },
    less: {
      production: {
        options: {
        },
        files: {
          'temp/app.css': 'app.less'
        }
      }
    },
    autoprefixer: {
      options: {
        browsers: ['ie >= 8', 'ff > 4', 'last 8 versions']
      },
      main: {
        src: 'temp/app.css'
      }
    },
    ngtemplates: {
      main: {
        options: {
            module: pkg.name,
            htmlmin:'<%= htmlmin.main.options %>'
        },
        src: [createFolderGlobs('*.html'),'!index.html','!_SpecRunner.html'],
        dest: 'temp/templates.js'
      }
    },
    copy: {
      main: {
        files: [
          {src: ['img/**'], dest: 'dist/'},
          {src: ['temp_data/**'], dest: 'dist/'},
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/fonts/'
          },
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/fonts/'
          },
          {
            src: ['locales/**'],
            dest: 'dist/'
          },
//           {src: ['bower_components/font-awesome/fonts/**'], dest: 'dist/',filter:'isFile',expand:true}
          //{src: ['bower_components/angular-ui-utils/ui-utils-ieshiv.min.js'], dest: 'dist/'},
          //{src: ['bower_components/select2/*.png','bower_components/select2/*.gif'], dest:'dist/css/',flatten:true,expand:true},
          //{src: ['bower_components/angular-mocks/angular-mocks.js'], dest: 'dist/'}
        ]
      }
    },
    dom_munger:{
      read: {
        options: {
          read:[
            {selector:'script[class="libnocompat"]',attribute:'src',writeto:'libnocompatjs'},
            {selector:'script[class="lib"]',attribute:'src',writeto:'libjs'},
            {selector:'script[class="app"]',attribute:'src',writeto:'appjs'},
            {selector:'link[rel="stylesheet"][data-concat!="false"]',attribute:'href',writeto:'appcss'}
          ]
        },
        src: 'index.html'
      },
      update: {
        options: {
          remove: ['script[data-remove!="false"]','link[data-remove!="false"]'],
          append: [
            {selector:'body',html:'<!--[if lte IE 8]><script src="libcompat-v1.0.11.min.js"></script><![endif]--><!--[if gte IE 9]><script src="libnocompat-v1.0.11.min.js"></script><![endif]--><!--[if !IE]><!--><script src="libnocompat-v1.0.11.min.js"></script><!--<![endif]-->'},
            {selector:'body',html:'<script src="lib-v1.0.11.min.js"></script>'},
            {selector:'body',html:'<script src="avConfig-v1.0.11.js"></script>'},
            {selector:'body',html:'<script src="app-v1.0.11.min.js"></script>'},
            {selector:'head',html:'<link rel="stylesheet" href="app-v1.0.11.full.min.css">'}
          ]
        },
        src:'index.html',
        dest: 'dist/index.html'
      }
    },
    cssmin: {
      main: {
        src:['temp/app.css','<%= dom_munger.data.appcss %>'],
        dest:'dist/app-v1.0.11.full.min.css'
      },
    },
    concat: {
      main: {
        files: {
          'temp/libcompat.js': [
             'vendor/jquery.compat/jquery-1.11.1.js',
             'vendor/json3/json-v3.3.2.js',
             'vendor/crypto/unsupportedBrowser.js'
          ],
          'temp/libnocompat.js': ['<%= dom_munger.data.libnocompatjs %>'],
          'temp/lib.js': ['<%= dom_munger.data.libjs %>'],
          'temp/app.js': ['<%= dom_munger.data.appjs %>','<%= ngtemplates.main.dest %>'],
          'dist/avConfig-v1.0.11.js': ['avConfig.js']
        }
      }
    },
    ngAnnotate: {
      main: {
        files: {
        'temp/app.js':['temp/app.js'],
        'temp/lib.js': ['temp/lib.js'],
        'temp/libnocompat.js': ['temp/libnocompat.js'],
        'temp/libcompat.js': ['temp/libcompat.js']
        }
      }
    },
    uglify: {
      main: {
        /*options:{
          mangle: false,
          compress: false,
          beautify: true
        },*/
        files: {
          'dist/app-v1.0.11.min.js': 'temp/app.js',
          'dist/lib-v1.0.11.min.js': 'temp/lib.js',
          'dist/libnocompat-v1.0.11.min.js': 'temp/libnocompat.js',
          'dist/libcompat-v1.0.11.min.js': 'temp/libcompat.js'
        }
      }
    },
    htmlmin: {
      main: {
        options: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          removeAttributeQuotes: false,
          removeComments: false,
          removeEmptyAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        },
        files: {
          'dist/index.html': 'dist/index.html'
        }
      }
    },
    imagemin: {
      main:{
        files: [{
          expand: true, cwd:'dist/',
          src:['**/{*.png,*.jpg}'],
          dest: 'dist/'
        }]
      }
    },
    karma: {
      options: {
        frameworks: ['jasmine'],
        files: [  //this files data is also updated in the watch handler, if updated change there too
          '<%= dom_munger.data.libnocompatjs %>',
          '<%= dom_munger.data.libjs %>',
          'avConfig.js',
          '<%= dom_munger.data.appjs %>',
          '<%= ngtemplates.main.dest %>',
          'bower_components/angular-mocks/angular-mocks.js',
          createFolderGlobs('*-spec.js')
        ],
        logLevel:'ERROR',
        reporters:['mocha'],
        autoWatch: false, //watching is handled by grunt-contrib-watch
        singleRun: true
      },
      all_tests: {
        browsers: ['PhantomJS','Chrome','Firefox']
      },
      during_watch: {
        browsers: ['PhantomJS']
      },
    },
    protractor: {
      options: {
        configFile: "node_modules/protractor/referenceConf.js", // Default config file
        keepAlive: true, // If false, the grunt process stops when the test fails.
        noColor: false, // If true, protractor will not use colors in its output.
        args: {
        // Arguments passed to the command
      }
    },
    //your_target: {   // Grunt requires at least one target to run so you can simply put 'all: {}' here too.
      all: {
      options: {
        configFile: "e2e.conf.js", // Target-specific config file
        args: {} // Target-specific arguments
      }
    },
  },

  });

  grunt.registerTask('build',['jshint','clean:before','less','autoprefixer','dom_munger','ngtemplates','cssmin','concat','ngAnnotate','uglify','copy','htmlmin','imagemin','clean:after']);
  grunt.registerTask('serve', ['dom_munger:read','jshint','connect', 'watch']);
  grunt.registerTask('test',['dom_munger:read','karma:all_tests']);

  grunt.event.on('watch', function(action, filepath) {
    //https://github.com/gruntjs/grunt-contrib-watch/issues/156

    var tasksToRun = [];

    if ((filepath.lastIndexOf('.json') !== -1 && filepath.lastIndexOf('.json') === filepath.length - 5) ||
        (filepath.lastIndexOf('.js') !== -1 && filepath.lastIndexOf('.js') === filepath.length - 3)) {

      //lint the changed js file
      grunt.config('jshint.main.src', filepath);
      tasksToRun.push('jshint');

      //find the appropriate unit t est for the changed file
      var spec = filepath;
      if (filepath.lastIndexOf('-spec.js') === -1 || filepath.lastIndexOf('-spec.js') !== filepath.length - 8) {
        spec = filepath.substring(0,filepath.length - 3) + '-spec.js';
      }

      //if the spec exists then lets run it
      if (grunt.file.exists(spec)) {
        var files = [].concat(grunt.config('dom_munger.data.libnocompatjs'));
        files.concat(grunt.config('dom_munger.data.libjs'));
        files.push('bower_components/angular-mocks/angular-mocks.js');
        files.push('avConfig.js');
        files.concat(grunt.config('dom_munger.data.appjs'));
        files.concat(grunt.config('ngtemplates.main.dest'));
        files.push(spec);
        grunt.config('karma.options.files', files);
        tasksToRun.push('karma:during_watch');
      }
    }

    //if index.html changed, we need to reread the <script> tags so our next run of karma
    //will have the correct environment
    if (filepath === 'index.html') {
      tasksToRun.push('dom_munger:read');
    }

    grunt.config('watch.main.tasks',tasksToRun);

  });

  grunt.loadNpmTasks('grunt-protractor-runner');
};
<|endoftext|>


<commit_before>const Promise = require('bluebird');
const getPath = require('get-value');

// helpers
const { serialize } = require('./redis');
const key = require('../redis-key');

// constants
const FIND_OWNER_REGEXP = /\[([^\]]+)\]/;
const {
  TRANSACTION_TYPE_RECURRING,
  TRANSACTION_TYPE_SALE,
  TRANSACTION_TYPE_3D,
  TRANSACTIONS_INDEX,
  TRANSACTIONS_COMMON_DATA,
} = require('../constants');

function getTransactionType(type) {
  switch (type) {
    case TRANSACTION_TYPE_RECURRING:
      return 'subscription';
    case TRANSACTION_TYPE_SALE:
      return 'sale';
    case TRANSACTION_TYPE_3D:
      return 'print';
    default:
      throw new Error('unsupported transaction type');
  }
}

// required:
// 1. type {Number}
// 2. id {String}
function saveCommon(data) {
  const { redis } = this;
  const transactionType = getTransactionType(data.type);

  // 1. add to common index
  // 2. add to transaction type index
  // 3. add to user type index
  // 4. add to user+transaction type index

  const { id } = data;
  const pipeline = redis.pipeline();
  const transactionTypeIndex = key(TRANSACTIONS_INDEX, transactionType);
  const userIndex = data.owner && key(TRANSACTIONS_INDEX, data.owner);
  const userTransactionTypeIndex = userIndex && key(userIndex, transactionType);

  // 5. store metadata data at this prefix
  const dataKey = key(TRANSACTIONS_COMMON_DATA, id);

  pipeline.sadd(TRANSACTIONS_INDEX, id);
  pipeline.sadd(transactionTypeIndex, id);
  pipeline.hmset(dataKey, serialize(data));

  if (userIndex) {
    pipeline.sadd(userIndex, id);
    pipeline.sadd(userTransactionTypeIndex, id);
  }

  return pipeline.exec().return(data);
}

function formatItemList({ items = [] } = {}) {
  return items.map(({
    name, price, quantity, currency,
  }) => (
    `${name} x${quantity} for ${parseFloat(price) * quantity} ${currency}.`
  )).join('\n');
}

function prepareDescription(amount, owner, state) {
  if (!amount) {
    return `${state} agreement with ${owner}`;
  }

  return `Recurring payment of ${amount.value} USD for ${owner}`;
}

// FIXME: retarded paypal bug, hopefully it is fixed in the future
function remapState(state) {
  return state === 'approved_symphony' ? 'approved' : state;
}

function parseSale(sale, owner) {
  // to catch errors automatically
  return Promise.try(() => {
    // reasonable default?
    const payer = getPath(sale, 'payer.payer_info.email', owner);
    const [transaction] = sale.transactions;
    const description = formatItemList(transaction.item_list);
    const type = description.indexOf('3d printing') >= 0 ? TRANSACTION_TYPE_3D : TRANSACTION_TYPE_SALE;

    return {
      id: sale.id,
      type,
      owner,
      payer,
      date: new Date(sale.create_time).getTime(),
      update_time: new Date(sale.update_time || sale.create_time).getTime(),
      amount: transaction.amount.total,
      currency: transaction.amount.currency,
      description,
      // Payment state. Must be set to one of the one of the following: created; approved; failed; canceled; expired; pending.
      // Value assigned by PayPal.
      status: remapState(sale.state),
    };
  });
}

function parseAgreementTransaction(transaction, owner, agreementId) {
  return Promise.try(() => ({
    id: transaction.transaction_id,
    type: TRANSACTION_TYPE_RECURRING,
    owner,
    agreementId,
    payer: transaction.payer_email || undefined,
    date: new Date(transaction.time_stamp).getTime(),
    amount: getPath(transaction, 'amount.value', '0.00'),
    description: prepareDescription(transaction.amount, owner, transaction.status),
    status: transaction.status,
  }));
}

function getOwner(sale) {
  const description = getPath(sale, 'transactions[0].item_list.items[0].name', false);
  const match = description && FIND_OWNER_REGEXP.exec(description);
  const result = match && match[1];
  return result || (sale.payer_info && sale.payer_info.email) || null;
}

module.exports = exports = {
  getOwner,
  saveCommon,
  parseSale,
  parseAgreementTransaction,
  remapState,
};
<commit_msg>fix: get-value path<commit_after>const Promise = require('bluebird');
const getPath = require('get-value');

// helpers
const { serialize } = require('./redis');
const key = require('../redis-key');

// constants
const FIND_OWNER_REGEXP = /\[([^\]]+)\]/;
const {
  TRANSACTION_TYPE_RECURRING,
  TRANSACTION_TYPE_SALE,
  TRANSACTION_TYPE_3D,
  TRANSACTIONS_INDEX,
  TRANSACTIONS_COMMON_DATA,
} = require('../constants');

function getTransactionType(type) {
  switch (type) {
    case TRANSACTION_TYPE_RECURRING:
      return 'subscription';
    case TRANSACTION_TYPE_SALE:
      return 'sale';
    case TRANSACTION_TYPE_3D:
      return 'print';
    default:
      throw new Error('unsupported transaction type');
  }
}

// required:
// 1. type {Number}
// 2. id {String}
function saveCommon(data) {
  const { redis } = this;
  const transactionType = getTransactionType(data.type);

  // 1. add to common index
  // 2. add to transaction type index
  // 3. add to user type index
  // 4. add to user+transaction type index

  const { id } = data;
  const pipeline = redis.pipeline();
  const transactionTypeIndex = key(TRANSACTIONS_INDEX, transactionType);
  const userIndex = data.owner && key(TRANSACTIONS_INDEX, data.owner);
  const userTransactionTypeIndex = userIndex && key(userIndex, transactionType);

  // 5. store metadata data at this prefix
  const dataKey = key(TRANSACTIONS_COMMON_DATA, id);

  pipeline.sadd(TRANSACTIONS_INDEX, id);
  pipeline.sadd(transactionTypeIndex, id);
  pipeline.hmset(dataKey, serialize(data));

  if (userIndex) {
    pipeline.sadd(userIndex, id);
    pipeline.sadd(userTransactionTypeIndex, id);
  }

  return pipeline.exec().return(data);
}

function formatItemList({ items = [] } = {}) {
  return items.map(({
    name, price, quantity, currency,
  }) => (
    `${name} x${quantity} for ${parseFloat(price) * quantity} ${currency}.`
  )).join('\n');
}

function prepareDescription(amount, owner, state) {
  if (!amount) {
    return `${state} agreement with ${owner}`;
  }

  return `Recurring payment of ${amount.value} USD for ${owner}`;
}

// FIXME: retarded paypal bug, hopefully it is fixed in the future
function remapState(state) {
  return state === 'approved_symphony' ? 'approved' : state;
}

function parseSale(sale, owner) {
  // to catch errors automatically
  return Promise.try(() => {
    // reasonable default?
    const payer = getPath(sale, 'payer.payer_info.email', owner);
    const [transaction] = sale.transactions;
    const description = formatItemList(transaction.item_list);
    const type = description.indexOf('3d printing') >= 0 ? TRANSACTION_TYPE_3D : TRANSACTION_TYPE_SALE;

    return {
      id: sale.id,
      type,
      owner,
      payer,
      date: new Date(sale.create_time).getTime(),
      update_time: new Date(sale.update_time || sale.create_time).getTime(),
      amount: transaction.amount.total,
      currency: transaction.amount.currency,
      description,
      // Payment state. Must be set to one of the one of the following: created; approved; failed; canceled; expired; pending.
      // Value assigned by PayPal.
      status: remapState(sale.state),
    };
  });
}

function parseAgreementTransaction(transaction, owner, agreementId) {
  return Promise.try(() => ({
    id: transaction.transaction_id,
    type: TRANSACTION_TYPE_RECURRING,
    owner,
    agreementId,
    payer: transaction.payer_email || undefined,
    date: new Date(transaction.time_stamp).getTime(),
    amount: getPath(transaction, 'amount.value', '0.00'),
    description: prepareDescription(transaction.amount, owner, transaction.status),
    status: transaction.status,
  }));
}

function getOwner(sale) {
  const description = getPath(sale, ['transactions', 0, 'item_list', 'items', 0, 'name'], false);
  const match = description && FIND_OWNER_REGEXP.exec(description);
  const result = match && match[1];
  return result || (sale.payer_info && sale.payer_info.email) || null;
}

module.exports = exports = {
  getOwner,
  saveCommon,
  parseSale,
  parseAgreementTransaction,
  remapState,
};
<|endoftext|>


<commit_before>Node.inject({
	getEditForm: function(param) {
		var form = new EditForm(this, { removable: true, previewable: true });
		if (param.children === undefined)
			param.children = true;
		if (param.resources === undefined)
			param.resources = true;
		form.addTab('node', param.tabLabel || 'Node',
			param.children ? {
				type: 'multiselect', name: 'children',
				label: param.children.label || 'Sub Pages',
				ordered: Base.pick(param.children.ordered, true),
				showOptions: Base.pick(param.children.showOptions, true),
				collection: this.all, value: this,
				prototypes: Base.pick(param.children.prototypes, 'Node'),
				movable: Base.pick(param.children.movable, true),
				editable: true, autoRemove: true, // Always
				size: Base.pick(param.children.size, 6)
			} : null,
			param.resources ? {
				type: 'multiselect', name: 'resources',
				label: param.resources.label || 'Resources',
				ordered: Base.pick(param.resources.ordered, true),
				showOptions: Base.pick(param.resources.showOptions, true),
				collection: this.allResources, value: this.resources,
				prototypes: Base.pick(param.resources.prototypes, 'Resource,Medium,Picture'),
				movable: Base.pick(param.resources.movable, true),
				editable: true, autoRemove: true, // Always
				size: Base.pick(param.resources.size, 6)
			} : null
		);
		return form;
	},

	isEditableBy: function(user) {
		return user && this.creator == user;
	},

	getUniqueChildName: function(object, name, maxLength) {
		name = name.trim().urlize();
		if (name.length > maxLength)
			name = name.substring(0, maxLength).trim('-');
		var baseName = name;
		var count = 1;
		while (true) {
			if (name) {
				var obj = this.all.get(name);
				if (!obj || obj == object)
					break;
			}
			// try a different number at the end of the name:
			name = (baseName ? baseName + '-' : '') + (count++);
		}
		return name;
	}
});
<commit_msg>- moveable and reviewable in Node#getEditForm are controllable through the param object. - Make hidden sub nodes visible while editing a node, by looking into the all collection as well.<commit_after>Node.inject({
	getEditForm: function(param) {
		var form = new EditForm(this, {
			removable: Base.pick(param.removable, true),
			previewable: Base.pick(param.previewable, true)
		});
		if (param.children === undefined)
			param.children = true;
		if (param.resources === undefined)
			param.resources = true;
		form.addTab('node', param.tabLabel || 'Node',
			param.children ? {
				type: 'multiselect', name: 'children',
				label: param.children.label || 'Sub Pages',
				ordered: Base.pick(param.children.ordered, true),
				showOptions: Base.pick(param.children.showOptions, true),
				collection: this.all, value: this,
				prototypes: Base.pick(param.children.prototypes, 'Node'),
				movable: Base.pick(param.children.movable, true),
				editable: true, autoRemove: true, // Always
				size: Base.pick(param.children.size, 6)
			} : null,
			param.resources ? {
				type: 'multiselect', name: 'resources',
				label: param.resources.label || 'Resources',
				ordered: Base.pick(param.resources.ordered, true),
				showOptions: Base.pick(param.resources.showOptions, true),
				collection: this.allResources, value: this.resources,
				prototypes: Base.pick(param.resources.prototypes, 'Resource,Medium,Picture'),
				movable: Base.pick(param.resources.movable, true),
				editable: true, autoRemove: true, // Always
				size: Base.pick(param.resources.size, 6)
			} : null
		);
		return form;
	},

	isEditableBy: function(user) {
		return user && this.creator == user;
	},

	getChildElement: function(name) {
		var obj = this.get(name);
		// Return hidden elements as well if we're editing
		if (!obj && User.canEdit(this))
			obj = this.all.get(name);
		return obj;
	},

	getUniqueChildName: function(object, name, maxLength) {
		name = name.trim().urlize();
		if (name.length > maxLength)
			name = name.substring(0, maxLength).trim('-');
		var baseName = name;
		var count = 1;
		while (true) {
			if (name) {
				var obj = this.all.get(name);
				if (!obj || obj == object)
					break;
			}
			// try a different number at the end of the name:
			name = (baseName ? baseName + '-' : '') + (count++);
		}
		return name;
	}
});
<|endoftext|>


<commit_before>/*
 Main entry point for Thali test frameworks coordinator server

 jx index.js "{\"devices\":{\"android\":\"3\",\"ios\":\"2\"},\"honorCount\":\"true\"} //
 */

'use strict';

var options = {
  transports: ['websocket']
};

//pingTimeout: 3599000,
//pingInterval: 60000,

var app = require('express')();
var http = require('http').Server(app)

var io = require('socket.io')(http,options);

process.on('uncaughtException', function(err) {
  console.log("We have an uncaught exception, good bye: " + JSON.stringify(err));
});

process.on('unhandledRejection', function(err) {
  console.log("We have an uncaught promise rejection, good bye: " + JSON.stringify(err));
});

//IPAddressToFile is left here for debugging purposes, it gives you quick way on seeing the IP address used
//var IPAddressToFile = require('./IPAddressToFile');
//IPAddressToFile();

var TestDevice = require('./TestDevice');
var PerfTestFramework = require('./PerfTestFramework');
var UnitTestFramework = require('./UnitTestFramework');
var devicesObject = JSON.parse(process.argv[2]); //

var timeOutValueToStart = 30000;// after 300 seconds of waiting we'll start even if we did not get desired amount of devices

var TestsFrameworks  = {};
TestsFrameworks.perftest =  {};
TestsFrameworks.perftest.android = new PerfTestFramework("Android",devicesObject.devices.android,devicesObject.honorCount);
TestsFrameworks.perftest.ios     = new PerfTestFramework("iOs",devicesObject.devices.ios,devicesObject.honorCount);
TestsFrameworks.unittest =  {};
TestsFrameworks.unittest.android = new UnitTestFramework("Android");
TestsFrameworks.unittest.ios     = new UnitTestFramework("iOs");

var weHaveStartedTesting = false;
var startTimerId = null;
if (!devicesObject.honorCount) {
  startTimerId = setTimeout(function () {
    console.log("-------------- We got wait done, now starting the testing process ------------------");
    weHaveStartedTesting = true;

    var perfAndCount = TestsFrameworks.perftest.android.getCount();
    var perfIosCount = TestsFrameworks.perftest.ios.getCount();
    var unitAndCount = TestsFrameworks.unittest.android.getCount();
    var unitIosCount = TestsFrameworks.unittest.ios.getCount();

    TestsFrameworks['perftest']['android'].startTest({"devices":{"ios":perfIosCount,"android":perfAndCount}});
    TestsFrameworks['perftest']['ios'].startTest({"devices":{"ios":perfIosCount,"android":perfAndCount}});
    TestsFrameworks['unittest']['android'].startTest({"devices":{"ios":unitIosCount,"android":unitAndCount}});
    TestsFrameworks['unittest']['ios'].startTest({"devices":{"ios":unitIosCount,"android":unitAndCount}});

  }, timeOutValueToStart);
}

io.on('connection', function (socket) {
  console.log('Got socket connection');

  socket.on('close', function(){
    console.log('Socket close');
  });

  socket.on('error', function(){
    console.log('Socket error');
  });

  socket.on('disconnect', function(){
    console.log('Socket disconnect');
  });

  socket.on('present', function (msg) {
    var presentObj = JSON.parse(msg);
    if (!presentObj.os || !presentObj.name || !presentObj.type) {
      console.log("malformed message");
      socket.emit('error', JSON.stringify({"errorDescription ": "malformed message"}));
      return;
    }

    if (weHaveStartedTesting) {
      var isThisTestFromAddedDevice = false;
      // each file will do its own present with different name
      //  and we need to register the names, so we can determine which tests are actually included
      // thus we need to check whether we would need to let these present pass, even though we have started
      // there is one connection, i.e. one socket for each device, so we use that to determine what to do
      if (presentObj.type == "unittest") {
        if (devicesObject.honorCount) {
          isThisTestFromAddedDevice = TestsFrameworks['unittest'][presentObj.os].isSocketAlreadyCounted(socket);
        }
      } else { //perf test
        isThisTestFromAddedDevice = TestsFrameworks['perftest'][presentObj.os].isDeviceAlreadyAdded(presentObj.name);
      }

      if (!isThisTestFromAddedDevice) {
        console.log("too late arrival");
        socket.emit('too_late', JSON.stringify({"timeout ": "malformed message"}));
        return;
      }
    }

    var newDevice = new TestDevice(socket, presentObj.name, presentObj.os, presentObj.type, presentObj.btaddress);
    TestsFrameworks[presentObj.type][presentObj.os].addDevice(newDevice, socket);

    // implemented by both unit & performance tests
    if (devicesObject.honorCount && !weHaveStartedTesting) {

      var androidCount = TestsFrameworks[presentObj.type]['android'].getCount();
      var iosCount = TestsFrameworks[presentObj.type]['ios'].getCount();

      if (devicesObject.devices.android <= androidCount
          && devicesObject.devices.ios <= iosCount) {
        weHaveStartedTesting = true;
        TestsFrameworks[presentObj.type]['android'].startTest({"devices": {"ios": iosCount, "android": androidCount}});
        TestsFrameworks[presentObj.type]['ios'].startTest({"devices": {"ios": iosCount, "android": androidCount}});
      }
    }

    // implemented by both unit & performance tests
    socket.on('disconnect', function () {
      if (!weHaveStartedTesting) {
        socket.emit('test_error', JSON.stringify({"timeout ": "message not acceptable in current Test Server state"}));
        return;
      }
      TestsFrameworks[presentObj.type][presentObj.os].removeDevice(newDevice);
    });

    //this event only happens with unit tests, presentObj.type == unittest
    socket.on('setup_unit_test', function (msg) {
      var msgData = JSON.parse(msg);
      if (msgData.name == newDevice.getName()) {
        TestsFrameworks[presentObj.type][presentObj.os].addTest(newDevice, msgData.test);
      }
    });

    //this event only happens with unit tests, presentObj.type == unittest
    socket.on('unit_test_done', function (msg) {
      if (!weHaveStartedTesting) {
        socket.emit('test_error', JSON.stringify({"timeout ": "message not acceptable in current Test Server state"}));
        return;
      }

      var msgData = JSON.parse(msg);
      if (msgData.name == newDevice.getName()) {
        TestsFrameworks[presentObj.type][presentObj.os].ClientStopEventReceived(msgData.name, msgData.test);
      }
    });

    //this event only happens with performance tests, presentObj.type == perftest
    socket.on('test data', function (data) {
      if (!weHaveStartedTesting) {
        socket.emit('test_error', JSON.stringify({"timeout ": "message not acceptable in current Test Server state"}));
        return;
      }
      TestsFrameworks[presentObj.type][presentObj.os].ClientDataReceived(newDevice.getName(), data);
    });
  });
});

app.get('/', function(req, res){
  console.log("HTTP get called");
  res.sendfile('index.html');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

<commit_msg>Removed unnecessary code.<commit_after>/*
 Main entry point for Thali test frameworks coordinator server

 jx index.js "{\"devices\":{\"android\":\"3\",\"ios\":\"2\"},\"honorCount\":\"true\"} //
 */

'use strict';

var options = {
  transports: ['websocket']
};

//pingTimeout: 3599000,
//pingInterval: 60000,

var app = require('express')();
var http = require('http').Server(app)

var io = require('socket.io')(http,options);

var TestDevice = require('./TestDevice');
var PerfTestFramework = require('./PerfTestFramework');
var UnitTestFramework = require('./UnitTestFramework');
var devicesObject = JSON.parse(process.argv[2]);

var timeOutValueToStart = 30000;// after 300 seconds of waiting we'll start even if we did not get desired amount of devices

var TestsFrameworks  = {};
TestsFrameworks.perftest =  {};
TestsFrameworks.perftest.android = new PerfTestFramework("Android",devicesObject.devices.android,devicesObject.honorCount);
TestsFrameworks.perftest.ios     = new PerfTestFramework("iOs",devicesObject.devices.ios,devicesObject.honorCount);
TestsFrameworks.unittest =  {};
TestsFrameworks.unittest.android = new UnitTestFramework("Android");
TestsFrameworks.unittest.ios     = new UnitTestFramework("iOs");

var weHaveStartedTesting = false;
var startTimerId = null;
if (!devicesObject.honorCount) {
  startTimerId = setTimeout(function () {
    console.log("-------------- We got wait done, now starting the testing process ------------------");
    weHaveStartedTesting = true;

    var perfAndCount = TestsFrameworks.perftest.android.getCount();
    var perfIosCount = TestsFrameworks.perftest.ios.getCount();
    var unitAndCount = TestsFrameworks.unittest.android.getCount();
    var unitIosCount = TestsFrameworks.unittest.ios.getCount();

    TestsFrameworks['perftest']['android'].startTest({"devices":{"ios":perfIosCount,"android":perfAndCount}});
    TestsFrameworks['perftest']['ios'].startTest({"devices":{"ios":perfIosCount,"android":perfAndCount}});
    TestsFrameworks['unittest']['android'].startTest({"devices":{"ios":unitIosCount,"android":unitAndCount}});
    TestsFrameworks['unittest']['ios'].startTest({"devices":{"ios":unitIosCount,"android":unitAndCount}});

  }, timeOutValueToStart);
}

io.on('connection', function (socket) {
  socket.on('present', function (msg) {
    var presentObj = JSON.parse(msg);
    if (!presentObj.os || !presentObj.name || !presentObj.type) {
      console.log("malformed message");
      socket.emit('error', JSON.stringify({"errorDescription ": "malformed message"}));
      return;
    }

    if (weHaveStartedTesting) {
      var isThisTestFromAddedDevice = false;
      // each file will do its own present with different name
      //  and we need to register the names, so we can determine which tests are actually included
      // thus we need to check whether we would need to let these present pass, even though we have started
      // there is one connection, i.e. one socket for each device, so we use that to determine what to do
      if (presentObj.type == "unittest") {
        if (devicesObject.honorCount) {
          isThisTestFromAddedDevice = TestsFrameworks['unittest'][presentObj.os].isSocketAlreadyCounted(socket);
        }
      } else { //perf test
        isThisTestFromAddedDevice = TestsFrameworks['perftest'][presentObj.os].isDeviceAlreadyAdded(presentObj.name);
      }

      if (!isThisTestFromAddedDevice) {
        console.log("too late arrival");
        socket.emit('too_late', JSON.stringify({"timeout ": "malformed message"}));
        return;
      }
    }

    var newDevice = new TestDevice(socket, presentObj.name, presentObj.os, presentObj.type, presentObj.btaddress);
    TestsFrameworks[presentObj.type][presentObj.os].addDevice(newDevice, socket);

    // implemented by both unit & performance tests
    if (devicesObject.honorCount && !weHaveStartedTesting) {

      var androidCount = TestsFrameworks[presentObj.type]['android'].getCount();
      var iosCount = TestsFrameworks[presentObj.type]['ios'].getCount();

      if (devicesObject.devices.android <= androidCount
          && devicesObject.devices.ios <= iosCount) {
        weHaveStartedTesting = true;
        TestsFrameworks[presentObj.type]['android'].startTest({"devices": {"ios": iosCount, "android": androidCount}});
        TestsFrameworks[presentObj.type]['ios'].startTest({"devices": {"ios": iosCount, "android": androidCount}});
      }
    }

    // implemented by both unit & performance tests
    socket.on('disconnect', function () {
      if (!weHaveStartedTesting) {
        socket.emit('test_error', JSON.stringify({"timeout ": "message not acceptable in current Test Server state"}));
        return;
      }
      TestsFrameworks[presentObj.type][presentObj.os].removeDevice(newDevice);
    });

    //this event only happens with unit tests, presentObj.type == unittest
    socket.on('setup_unit_test', function (msg) {
      var msgData = JSON.parse(msg);
      if (msgData.name == newDevice.getName()) {
        TestsFrameworks[presentObj.type][presentObj.os].addTest(newDevice, msgData.test);
      }
    });

    //this event only happens with unit tests, presentObj.type == unittest
    socket.on('unit_test_done', function (msg) {
      if (!weHaveStartedTesting) {
        socket.emit('test_error', JSON.stringify({"timeout ": "message not acceptable in current Test Server state"}));
        return;
      }

      var msgData = JSON.parse(msg);
      if (msgData.name == newDevice.getName()) {
        TestsFrameworks[presentObj.type][presentObj.os].ClientStopEventReceived(msgData.name, msgData.test);
      }
    });

    //this event only happens with performance tests, presentObj.type == perftest
    socket.on('test data', function (data) {
      if (!weHaveStartedTesting) {
        socket.emit('test_error', JSON.stringify({"timeout ": "message not acceptable in current Test Server state"}));
        return;
      }
      TestsFrameworks[presentObj.type][presentObj.os].ClientDataReceived(newDevice.getName(), data);
    });
  });
});

app.get('/', function(req, res){
  console.log("HTTP get called");
  res.sendfile('index.html');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

<|endoftext|>


<commit_before>module.exports = function(grunt) {
    var sweet = require("./lib/sweet");
    var path = require("path");

    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-mocha-test');

    var moduleCache = {};

    grunt.initConfig({
        build: {
            options: {
                modules: []
            },
            sweetjs: {
                src: "src/*.js",
                dest: ["build/lib/", "browser/scripts/"]
            },
            test: {
                options: {
                    // bug in source mapping is causing test
                    // compilation to fail
                    sourceMap: false
                },
                src: "test/*.js",
                dest: "build/"
            }
        },
        copy: {
            buildMacros: {
                src: "macros/*",
                dest: "build/macros/"
            },

            browserMacros: {
                src: "macros/*",
                dest: "browser/scripts/"
            },

            dist: {
                src: "build/lib/*.js",
                dest: "lib/"
            }
        },
        mochaTest: {
            test: {
                src: ["build/*.js"]
            }
        },
        jshint: {
            all: ["build/lib/*.js"]
        }
    });

    grunt.registerMultiTask("build", function() {
        var options = this.options({
            modules: [],
            sourceMap: true
        });

        var moduleSrc = options.modules.map(function(m) {
            return moduleCache[m] || (moduleCache[m] = readModuleFromCwd(m));
        }).join("\n");

        this.files.forEach(function(f) {
            var dest = Array.isArray(f.dest) ? f.dest : [f.dest];
            grunt.log.writeln("output to " + dest.join(", "));

            f.src.forEach(function(file) {
                grunt.log.writeln("compiling " + file);

                var code = moduleSrc + "\n" + grunt.file.read(file);
                var output = sweet.compile(code, {
                    sourceMap: options.sourceMap,
                    filename: file
                });

                dest.forEach(function(dest) {
                    // concatenated module and original source
                    grunt.file.write(dest + path.basename(file) + ".original",
                                     code);
                    // macro expanded result
                    grunt.file.write(dest + path.basename(file),
                                     output.code);
                    if (options.sourceMap) {
                        // sourcemap
                        grunt.file.write(dest + path.basename(file) + ".map",
                                         output.sourceMap);

                    }
                });
            })
        });

    });

    grunt.registerTask("dist", ["build:sweetjs", "copy:dist"]);

    grunt.registerTask("default", ["build",
                                   "copy:buildMacros",
                                   "copy:browserMacros",
                                   "mochaTest"]);

    function readModuleFromCwd(mod) {
        var cwd = process.cwd();
        var Module = module.constructor;
        var mockModule = {
            id: cwd + '/$sweet-loader.js',
            filename: '$sweet-loader.js',
            paths: /^\.\/|\.\./.test(cwd) ? [cwd] : Module._nodeModulePaths(cwd)
        };
        var path = Module._resolveFilename(mod, mockModule)
        return grunt.file.read(path);
    }
};
<commit_msg>mocha no-colors<commit_after>module.exports = function(grunt) {
    var sweet = require("./lib/sweet");
    var path = require("path");

    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-mocha-test');

    var moduleCache = {};

    grunt.initConfig({
        build: {
            options: {
                modules: []
            },
            sweetjs: {
                src: "src/*.js",
                dest: ["build/lib/", "browser/scripts/"]
            },
            test: {
                options: {
                    // bug in source mapping is causing test
                    // compilation to fail
                    sourceMap: false
                },
                src: "test/*.js",
                dest: "build/"
            }
        },
        copy: {
            buildMacros: {
                src: "macros/*",
                dest: "build/macros/"
            },

            browserMacros: {
                src: "macros/*",
                dest: "browser/scripts/"
            },

            dist: {
                src: "build/lib/*.js",
                dest: "lib/"
            }
        },
        mochaTest: {
            test: {
                options:{
                    colors: !grunt.option('no-color')
                },
                src: ["build/*.js"]
            }
        },
        jshint: {
            all: ["build/lib/*.js"]
        }
    });

    grunt.registerMultiTask("build", function() {
        var options = this.options({
            modules: [],
            sourceMap: true
        });

        var moduleSrc = options.modules.map(function(m) {
            return moduleCache[m] || (moduleCache[m] = readModuleFromCwd(m));
        }).join("\n");

        this.files.forEach(function(f) {
            var dest = Array.isArray(f.dest) ? f.dest : [f.dest];
            grunt.log.writeln("output to " + dest.join(", "));

            f.src.forEach(function(file) {
                grunt.log.writeln("compiling " + file);

                var code = moduleSrc + "\n" + grunt.file.read(file);
                var output = sweet.compile(code, {
                    sourceMap: options.sourceMap,
                    filename: file
                });

                dest.forEach(function(dest) {
                    // concatenated module and original source
                    grunt.file.write(dest + path.basename(file) + ".original",
                                     code);
                    // macro expanded result
                    grunt.file.write(dest + path.basename(file),
                                     output.code);
                    if (options.sourceMap) {
                        // sourcemap
                        grunt.file.write(dest + path.basename(file) + ".map",
                                         output.sourceMap);

                    }
                });
            })
        });

    });

    grunt.registerTask("dist", ["build:sweetjs", "copy:dist"]);

    grunt.registerTask("default", ["build",
                                   "copy:buildMacros",
                                   "copy:browserMacros",
                                   "mochaTest"]);

    function readModuleFromCwd(mod) {
        var cwd = process.cwd();
        var Module = module.constructor;
        var mockModule = {
            id: cwd + '/$sweet-loader.js',
            filename: '$sweet-loader.js',
            paths: /^\.\/|\.\./.test(cwd) ? [cwd] : Module._nodeModulePaths(cwd)
        };
        var path = Module._resolveFilename(mod, mockModule)
        return grunt.file.read(path);
    }
};
<|endoftext|>


<commit_before>import { keys } from './object.js';

export default function validateKeys ( object, allowedKeys ) {
	const actualKeys = keys( object );

	for ( let key of actualKeys ) {
		if ( allowedKeys.indexOf( key ) < 0 ) {
			return new Error(
				`Unexpected key '${ key }' found, expected one of: ${ allowedKeys.join( ', ' ) }`
			);
		}
	}
}
<commit_msg>avoid for...of<commit_after>import { keys } from './object.js';

export default function validateKeys ( object, allowedKeys ) {
	const actualKeys = keys( object );

	let i = actualKeys.length;

	while ( i-- ) {
		const key = actualKeys[i];

		if ( allowedKeys.indexOf( key ) === -1 ) {
			return new Error(
				`Unexpected key '${ key }' found, expected one of: ${ allowedKeys.join( ', ' ) }`
			);
		}
	}
}
<|endoftext|>


<commit_before>import _throttle from 'lodash-es/throttle';

import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';
import {
    select as d3_select
} from 'd3-selection';

import { utilArrayIdentical } from '../util/array';
import { utilFastMouse } from '../util';
import { osmEntity, osmNote, QAItem } from '../osm';
import { services } from '../services';
import { uiDataEditor } from './data_editor';
import { uiFeatureList } from './feature_list';
import { uiInspector } from './inspector';
import { uiImproveOsmEditor } from './improveOSM_editor';
import { uiKeepRightEditor } from './keepRight_editor';
import { uiOsmoseEditor } from './osmose_editor';
import { uiNoteEditor } from './note_editor';
import { localizer } from '../core/localizer';

export function uiSidebar(context) {
    var inspector = uiInspector(context);
    var dataEditor = uiDataEditor(context);
    var noteEditor = uiNoteEditor(context);
    var improveOsmEditor = uiImproveOsmEditor(context);
    var keepRightEditor = uiKeepRightEditor(context);
    var osmoseEditor = uiOsmoseEditor(context);
    var _current;
    var _wasData = false;
    var _wasNote = false;
    var _wasQaItem = false;

    // use pointer events on supported platforms; fallback to mouse events
    var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';

    function sidebar(selection) {
        var container = context.container();
        var minWidth = 240;
        var sidebarWidth;
        var containerWidth;
        var dragOffset;

        // Set the initial width constraints
        selection
            .style('min-width', minWidth + 'px')
            .style('max-width', '400px')
            .style('width', '33.3333%');

        var resizer = selection
            .append('div')
            .attr('class', 'sidebar-resizer')
            .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);

        var downPointerId, lastClientX, containerLocGetter;

        function pointerdown(d3_event) {
            if (downPointerId) return;

            if ('button' in d3_event && d3_event.button !== 0) return;

            downPointerId = d3_event.pointerId || 'mouse';

            lastClientX = d3_event.clientX;

            containerLocGetter = utilFastMouse(container.node());

            // offset from edge of sidebar-resizer
            dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;

            sidebarWidth = selection.node().getBoundingClientRect().width;
            containerWidth = container.node().getBoundingClientRect().width;
            var widthPct = (sidebarWidth / containerWidth) * 100;
            selection
                .style('width', widthPct + '%')    // lock in current width
                .style('max-width', '85%');        // but allow larger widths

            resizer.classed('dragging', true);

            d3_select(window)
                .on('touchmove.sidebar-resizer', function(d3_event) {
                    // disable page scrolling while resizing on touch input
                    d3_event.preventDefault();
                }, { passive: false })
                .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
                .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
        }

        function pointermove(d3_event) {

            if (downPointerId !== (d3_event.pointerId || 'mouse')) return;

            d3_event.preventDefault();

            var dx = d3_event.clientX - lastClientX;

            lastClientX = d3_event.clientX;

            var isRTL = (localizer.textDirection() === 'rtl');
            var scaleX = isRTL ? 0 : 1;
            var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';

            var x = containerLocGetter(d3_event)[0] - dragOffset;
            sidebarWidth = isRTL ? containerWidth - x : x;

            var isCollapsed = selection.classed('collapsed');
            var shouldCollapse = sidebarWidth < minWidth;

            selection.classed('collapsed', shouldCollapse);

            if (shouldCollapse) {
                if (!isCollapsed) {
                    selection
                        .style(xMarginProperty, '-400px')
                        .style('width', '400px');

                    context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
                }

            } else {
                var widthPct = (sidebarWidth / containerWidth) * 100;
                selection
                    .style(xMarginProperty, null)
                    .style('width', widthPct + '%');

                if (isCollapsed) {
                    context.ui().onResize([-sidebarWidth * scaleX, 0]);
                } else {
                    context.ui().onResize([-dx * scaleX, 0]);
                }
            }
        }

        function pointerup(d3_event) {
            if (downPointerId !== (d3_event.pointerId || 'mouse')) return;

            downPointerId = null;

            resizer.classed('dragging', false);

            d3_select(window)
                .on('touchmove.sidebar-resizer', null)
                .on(_pointerPrefix + 'move.sidebar-resizer', null)
                .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
        }

        var featureListWrap = selection
            .append('div')
            .attr('class', 'feature-list-pane')
            .call(uiFeatureList(context));

        var inspectorWrap = selection
            .append('div')
            .attr('class', 'inspector-hidden inspector-wrap');

        var hoverModeSelect = function(targets) {
            context.container().selectAll('.feature-list-item').classed('hover', false);

            if (context.selectedIDs().length > 1 &&
                targets && targets.length) {

                var elements = context.container().selectAll('.feature-list-item')
                    .filter(function (node) {
                        return targets.indexOf(node) !== -1;
                    });

                if (!elements.empty()) {
                    elements.classed('hover', true);
                }
            }
        };

        sidebar.hoverModeSelect = _throttle(hoverModeSelect, 200);

        function hover(targets) {
            var datum = targets && targets.length && targets[0];
            if (datum && datum.__featurehash__) {   // hovering on data
                _wasData = true;
                sidebar
                    .show(dataEditor.datum(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (datum instanceof osmNote) {
                if (context.mode().id === 'drag-note') return;
                _wasNote = true;

                var osm = services.osm;
                if (osm) {
                    datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
                }

                sidebar
                    .show(noteEditor.note(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (datum instanceof QAItem) {
                _wasQaItem = true;

                var errService = services[datum.service];
                if (errService) {
                    // marker may contain stale data - get latest
                    datum = errService.getError(datum.id);
                }

                // Currently only three possible services
                var errEditor;
                if (datum.service === 'keepRight') {
                    errEditor = keepRightEditor;
                } else if (datum.service === 'osmose') {
                    errEditor = osmoseEditor;
                } else {
                    errEditor = improveOsmEditor;
                }

                context.container().selectAll('.qaItem.' + datum.service)
                    .classed('hover', function(d) { return d.id === datum.id; });

                sidebar
                    .show(errEditor.error(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (!_current && (datum instanceof osmEntity)) {
                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', true);

                if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
                    inspector
                        .state('hover')
                        .entityIDs([datum.id])
                        .newFeature(false);

                    inspectorWrap
                        .call(inspector);
                }

            } else if (!_current) {
                featureListWrap
                    .classed('inspector-hidden', false);
                inspectorWrap
                    .classed('inspector-hidden', true);
                inspector
                    .state('hide');

            } else if (_wasData || _wasNote || _wasQaItem) {
                _wasNote = false;
                _wasData = false;
                _wasQaItem = false;
                context.container().selectAll('.note').classed('hover', false);
                context.container().selectAll('.qaItem').classed('hover', false);
                sidebar.hide();
            }
        }

        sidebar.hover = _throttle(hover, 200);

        sidebar.intersects = function(extent) {
            var rect = selection.node().getBoundingClientRect();
            return extent.intersects([
                context.projection.invert([0, rect.height]),
                context.projection.invert([rect.width, 0])
            ]);
        };

        sidebar.select = function(ids, newFeature) {
            sidebar.hide();

            if (ids && ids.length) {

                var entity = ids.length === 1 && context.entity(ids[0]);
                if (entity && newFeature && selection.classed('collapsed')) {
                    // uncollapse the sidebar
                    var extent = entity.extent(context.graph());
                    sidebar.expand(sidebar.intersects(extent));
                }

                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', false);

                // reload the UI even if the ids are the same since the entities
                // themselves may have changed
                inspector
                    .state('select')
                    .entityIDs(ids)
                    .newFeature(newFeature);

                inspectorWrap
                    .call(inspector);

            } else {
                inspector
                    .state('hide');
            }
        };

        sidebar.showPresetList = function() {
            inspector.showList();
        };

        sidebar.show = function(component, element) {
            featureListWrap
                .classed('inspector-hidden', true);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = selection
                .append('div')
                .attr('class', 'sidebar-component')
                .call(component, element);
        };

        sidebar.hide = function() {
            featureListWrap
                .classed('inspector-hidden', false);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = null;
        };

        sidebar.expand = function(moveMap) {
            if (selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.collapse = function(moveMap) {
            if (!selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.toggle = function(d3_event, moveMap) {
            var e = d3_event;
            if (e && e.sourceEvent) {
                e.sourceEvent.preventDefault();
            } else if (e) {
                e.preventDefault();
            }

            // Don't allow sidebar to toggle when the user is in the walkthrough.
            if (context.inIntro()) return;

            var isCollapsed = selection.classed('collapsed');
            var isCollapsing = !isCollapsed;
            var isRTL = (localizer.textDirection() === 'rtl');
            var scaleX = isRTL ? 0 : 1;
            var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';

            sidebarWidth = selection.node().getBoundingClientRect().width;

            // switch from % to px
            selection.style('width', sidebarWidth + 'px');

            var startMargin, endMargin, lastMargin;
            if (isCollapsing) {
                startMargin = lastMargin = 0;
                endMargin = -sidebarWidth;
            } else {
                startMargin = lastMargin = -sidebarWidth;
                endMargin = 0;
            }

            selection.transition()
                .style(xMarginProperty, endMargin + 'px')
                .tween('panner', function() {
                    var i = d3_interpolateNumber(startMargin, endMargin);
                    return function(t) {
                        var dx = lastMargin - Math.round(i(t));
                        lastMargin = lastMargin - dx;
                        context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
                    };
                })
                .on('end', function() {
                    selection.classed('collapsed', isCollapsing);

                    // switch back from px to %
                    if (!isCollapsing) {
                        var containerWidth = container.node().getBoundingClientRect().width;
                        var widthPct = (sidebarWidth / containerWidth) * 100;
                        selection
                            .style(xMarginProperty, null)
                            .style('width', widthPct + '%');
                    }
                });
        };

        // toggle the sidebar collapse when double-clicking the resizer
        resizer.on('dblclick', sidebar.toggle);

        // ensure hover sidebar is closed when zooming out beyond editable zoom
        context.map().on('crossEditableZoom.sidebar', function(within) {
            if (!within && !selection.select('.inspector-hover').empty()) {
                hover([]);
            }
        });
    }

    sidebar.showPresetList = function() {};
    sidebar.hover = function() {};
    sidebar.hover.cancel = function() {};
    sidebar.intersects = function() {};
    sidebar.select = function() {};
    sidebar.show = function() {};
    sidebar.hide = function() {};
    sidebar.expand = function() {};
    sidebar.collapse = function() {};
    sidebar.toggle = function() {};

    return sidebar;
}
<commit_msg>Fix issue with expanding the sidebar after drawing a generic feature<commit_after>import _throttle from 'lodash-es/throttle';

import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';
import {
    select as d3_select
} from 'd3-selection';

import { utilArrayIdentical } from '../util/array';
import { utilFastMouse } from '../util';
import { osmEntity, osmNote, QAItem } from '../osm';
import { services } from '../services';
import { uiDataEditor } from './data_editor';
import { uiFeatureList } from './feature_list';
import { uiInspector } from './inspector';
import { uiImproveOsmEditor } from './improveOSM_editor';
import { uiKeepRightEditor } from './keepRight_editor';
import { uiOsmoseEditor } from './osmose_editor';
import { uiNoteEditor } from './note_editor';
import { localizer } from '../core/localizer';

export function uiSidebar(context) {
    var inspector = uiInspector(context);
    var dataEditor = uiDataEditor(context);
    var noteEditor = uiNoteEditor(context);
    var improveOsmEditor = uiImproveOsmEditor(context);
    var keepRightEditor = uiKeepRightEditor(context);
    var osmoseEditor = uiOsmoseEditor(context);
    var _current;
    var _wasData = false;
    var _wasNote = false;
    var _wasQaItem = false;

    // use pointer events on supported platforms; fallback to mouse events
    var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';

    function sidebar(selection) {
        var container = context.container();
        var minWidth = 240;
        var sidebarWidth;
        var containerWidth;
        var dragOffset;

        // Set the initial width constraints
        selection
            .style('min-width', minWidth + 'px')
            .style('max-width', '400px')
            .style('width', '33.3333%');

        var resizer = selection
            .append('div')
            .attr('class', 'sidebar-resizer')
            .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);

        var downPointerId, lastClientX, containerLocGetter;

        function pointerdown(d3_event) {
            if (downPointerId) return;

            if ('button' in d3_event && d3_event.button !== 0) return;

            downPointerId = d3_event.pointerId || 'mouse';

            lastClientX = d3_event.clientX;

            containerLocGetter = utilFastMouse(container.node());

            // offset from edge of sidebar-resizer
            dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;

            sidebarWidth = selection.node().getBoundingClientRect().width;
            containerWidth = container.node().getBoundingClientRect().width;
            var widthPct = (sidebarWidth / containerWidth) * 100;
            selection
                .style('width', widthPct + '%')    // lock in current width
                .style('max-width', '85%');        // but allow larger widths

            resizer.classed('dragging', true);

            d3_select(window)
                .on('touchmove.sidebar-resizer', function(d3_event) {
                    // disable page scrolling while resizing on touch input
                    d3_event.preventDefault();
                }, { passive: false })
                .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
                .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
        }

        function pointermove(d3_event) {

            if (downPointerId !== (d3_event.pointerId || 'mouse')) return;

            d3_event.preventDefault();

            var dx = d3_event.clientX - lastClientX;

            lastClientX = d3_event.clientX;

            var isRTL = (localizer.textDirection() === 'rtl');
            var scaleX = isRTL ? 0 : 1;
            var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';

            var x = containerLocGetter(d3_event)[0] - dragOffset;
            sidebarWidth = isRTL ? containerWidth - x : x;

            var isCollapsed = selection.classed('collapsed');
            var shouldCollapse = sidebarWidth < minWidth;

            selection.classed('collapsed', shouldCollapse);

            if (shouldCollapse) {
                if (!isCollapsed) {
                    selection
                        .style(xMarginProperty, '-400px')
                        .style('width', '400px');

                    context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
                }

            } else {
                var widthPct = (sidebarWidth / containerWidth) * 100;
                selection
                    .style(xMarginProperty, null)
                    .style('width', widthPct + '%');

                if (isCollapsed) {
                    context.ui().onResize([-sidebarWidth * scaleX, 0]);
                } else {
                    context.ui().onResize([-dx * scaleX, 0]);
                }
            }
        }

        function pointerup(d3_event) {
            if (downPointerId !== (d3_event.pointerId || 'mouse')) return;

            downPointerId = null;

            resizer.classed('dragging', false);

            d3_select(window)
                .on('touchmove.sidebar-resizer', null)
                .on(_pointerPrefix + 'move.sidebar-resizer', null)
                .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
        }

        var featureListWrap = selection
            .append('div')
            .attr('class', 'feature-list-pane')
            .call(uiFeatureList(context));

        var inspectorWrap = selection
            .append('div')
            .attr('class', 'inspector-hidden inspector-wrap');

        var hoverModeSelect = function(targets) {
            context.container().selectAll('.feature-list-item').classed('hover', false);

            if (context.selectedIDs().length > 1 &&
                targets && targets.length) {

                var elements = context.container().selectAll('.feature-list-item')
                    .filter(function (node) {
                        return targets.indexOf(node) !== -1;
                    });

                if (!elements.empty()) {
                    elements.classed('hover', true);
                }
            }
        };

        sidebar.hoverModeSelect = _throttle(hoverModeSelect, 200);

        function hover(targets) {
            var datum = targets && targets.length && targets[0];
            if (datum && datum.__featurehash__) {   // hovering on data
                _wasData = true;
                sidebar
                    .show(dataEditor.datum(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (datum instanceof osmNote) {
                if (context.mode().id === 'drag-note') return;
                _wasNote = true;

                var osm = services.osm;
                if (osm) {
                    datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
                }

                sidebar
                    .show(noteEditor.note(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (datum instanceof QAItem) {
                _wasQaItem = true;

                var errService = services[datum.service];
                if (errService) {
                    // marker may contain stale data - get latest
                    datum = errService.getError(datum.id);
                }

                // Currently only three possible services
                var errEditor;
                if (datum.service === 'keepRight') {
                    errEditor = keepRightEditor;
                } else if (datum.service === 'osmose') {
                    errEditor = osmoseEditor;
                } else {
                    errEditor = improveOsmEditor;
                }

                context.container().selectAll('.qaItem.' + datum.service)
                    .classed('hover', function(d) { return d.id === datum.id; });

                sidebar
                    .show(errEditor.error(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (!_current && (datum instanceof osmEntity)) {
                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', true);

                if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
                    inspector
                        .state('hover')
                        .entityIDs([datum.id])
                        .newFeature(false);

                    inspectorWrap
                        .call(inspector);
                }

            } else if (!_current) {
                featureListWrap
                    .classed('inspector-hidden', false);
                inspectorWrap
                    .classed('inspector-hidden', true);
                inspector
                    .state('hide');

            } else if (_wasData || _wasNote || _wasQaItem) {
                _wasNote = false;
                _wasData = false;
                _wasQaItem = false;
                context.container().selectAll('.note').classed('hover', false);
                context.container().selectAll('.qaItem').classed('hover', false);
                sidebar.hide();
            }
        }

        sidebar.hover = _throttle(hover, 200);

        sidebar.intersects = function(extent) {
            var rect = selection.node().getBoundingClientRect();
            return extent.intersects([
                context.projection.invert([0, rect.height]),
                context.projection.invert([rect.width, 0])
            ]);
        };

        sidebar.select = function(ids, newFeature) {
            sidebar.hide();

            if (ids && ids.length) {

                var entity = ids.length === 1 && context.entity(ids[0]);
                if (entity && newFeature && selection.classed('collapsed')) {
                    // uncollapse the sidebar
                    var extent = entity.extent(context.graph());
                    sidebar.expand(sidebar.intersects(extent));
                }

                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', false);

                // reload the UI even if the ids are the same since the entities
                // themselves may have changed
                inspector
                    .state('select')
                    .entityIDs(ids)
                    .newFeature(newFeature);

                inspectorWrap
                    .call(inspector);

            } else {
                inspector
                    .state('hide');
            }
        };

        sidebar.showPresetList = function() {
            inspector.showList();
        };

        sidebar.show = function(component, element) {
            featureListWrap
                .classed('inspector-hidden', true);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = selection
                .append('div')
                .attr('class', 'sidebar-component')
                .call(component, element);
        };

        sidebar.hide = function() {
            featureListWrap
                .classed('inspector-hidden', false);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = null;
        };

        sidebar.expand = function(moveMap) {
            if (selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.collapse = function(moveMap) {
            if (!selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.toggle = function(moveMap) {

            // Don't allow sidebar to toggle when the user is in the walkthrough.
            if (context.inIntro()) return;

            var isCollapsed = selection.classed('collapsed');
            var isCollapsing = !isCollapsed;
            var isRTL = (localizer.textDirection() === 'rtl');
            var scaleX = isRTL ? 0 : 1;
            var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';

            sidebarWidth = selection.node().getBoundingClientRect().width;

            // switch from % to px
            selection.style('width', sidebarWidth + 'px');

            var startMargin, endMargin, lastMargin;
            if (isCollapsing) {
                startMargin = lastMargin = 0;
                endMargin = -sidebarWidth;
            } else {
                startMargin = lastMargin = -sidebarWidth;
                endMargin = 0;
            }

            selection.transition()
                .style(xMarginProperty, endMargin + 'px')
                .tween('panner', function() {
                    var i = d3_interpolateNumber(startMargin, endMargin);
                    return function(t) {
                        var dx = lastMargin - Math.round(i(t));
                        lastMargin = lastMargin - dx;
                        context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
                    };
                })
                .on('end', function() {
                    selection.classed('collapsed', isCollapsing);

                    // switch back from px to %
                    if (!isCollapsing) {
                        var containerWidth = container.node().getBoundingClientRect().width;
                        var widthPct = (sidebarWidth / containerWidth) * 100;
                        selection
                            .style(xMarginProperty, null)
                            .style('width', widthPct + '%');
                    }
                });
        };

        // toggle the sidebar collapse when double-clicking the resizer
        resizer.on('dblclick', function(d3_event) {
            d3_event.preventDefault();
            if (d3_event.sourceEvent) {
                e.sourceEvent.preventDefault();
            }
            sidebar.toggle();
        });

        // ensure hover sidebar is closed when zooming out beyond editable zoom
        context.map().on('crossEditableZoom.sidebar', function(within) {
            if (!within && !selection.select('.inspector-hover').empty()) {
                hover([]);
            }
        });
    }

    sidebar.showPresetList = function() {};
    sidebar.hover = function() {};
    sidebar.hover.cancel = function() {};
    sidebar.intersects = function() {};
    sidebar.select = function() {};
    sidebar.show = function() {};
    sidebar.hide = function() {};
    sidebar.expand = function() {};
    sidebar.collapse = function() {};
    sidebar.toggle = function() {};

    return sidebar;
}
<|endoftext|>


<commit_before>// Docs aren't that great to find. Mostly JAVA based. Here are few helpful resources:
// - https://www.browserstack.com/automate/node#testing-frameworks
// - http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/firefox/index_exports_Driver.html
// - https://github.com/SeleniumHQ/selenium/blob/c10e8a955883f004452cdde18096d70738397788/javascript/node/selenium-webdriver/test/upload_test.js
//
// - https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs
// - http://seleniumhq.github.io/selenium/docs/api/javascript/
// - http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/firefox/index.html
// - http://selenium.googlecode.com/git/docs/api/javascript/namespace_webdriver_By.html
// - http://selenium.googlecode.com/git/docs/api/javascript/class_webdriver_WebElement.html

require('babel-register')

var webdriver = require('selenium-webdriver')
var remote = require('selenium-webdriver/remote')

var username = process.env.USERNAME
var accessKey = process.env.ACCESS_KEY

// if accessKey is supplied as env variable, this is a remote Saucelabs test
var isRemoteTest = accessKey ? true : ''
var host = isRemoteTest ? 'http://uppy.io' : 'http://localhost:4000'

// FYI: old Chrome on Windows XP,
// Opera 12 on Linux — didn’t pass
var platforms = [
  // { browser: 'Opera', version: '12', os: 'Linux' },
  // { browser: 'iphone', version: '9.2', os: 'OS X 10.10' },
  { browser: 'firefox', version: '34.0', os: 'Windows 7' },
  { browser: 'chrome', version: '48.0', os: 'Windows XP' }
]

var tests = [
  require('./multipart.spec.js'),
  require('./i18n.spec.js')
  // require('./dragdrop.spec.js')
]

function buildDriver (platform) {
  var driver
  if (isRemoteTest) {
    driver = new webdriver
        .Builder()
        .withCapabilities({
          'browserName': platform.browser,
          'platform': platform.os,
          'version': platform.version,
          'username': username,
          'accessKey': accessKey
        })
        .usingServer('http://' + username + ':' + accessKey +
                    '@ondemand.saucelabs.com:80/wd/hub')
        .build()
    driver.setFileDetector(new remote.FileDetector())
  } else {
    driver = new webdriver
      .Builder()
      .forBrowser('firefox')
      .build()
  }
  return driver
}

if (isRemoteTest) {
  platforms.forEach(function (platform) {
    tests.forEach(function (test) {
      var driver = buildDriver(platform)
      test(driver, platform, host)
    })
  })
} else {
  tests.forEach(function (test) {
    var driver = buildDriver()
    test(driver, { browser: 'Firefox', version: 'Version', os: 'Local' }, host)
  })
}
<commit_msg>SAUCELABS_ prefix<commit_after>// Docs aren't that great to find. Mostly JAVA based. Here are few helpful resources:
// - https://www.browserstack.com/automate/node#testing-frameworks
// - http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/firefox/index_exports_Driver.html
// - https://github.com/SeleniumHQ/selenium/blob/c10e8a955883f004452cdde18096d70738397788/javascript/node/selenium-webdriver/test/upload_test.js
//
// - https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs
// - http://seleniumhq.github.io/selenium/docs/api/javascript/
// - http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/firefox/index.html
// - http://selenium.googlecode.com/git/docs/api/javascript/namespace_webdriver_By.html
// - http://selenium.googlecode.com/git/docs/api/javascript/class_webdriver_WebElement.html

require('babel-register')

var webdriver = require('selenium-webdriver')
var remote = require('selenium-webdriver/remote')

var username = process.env.SAUCELABS_USERNAME
var accessKey = process.env.SAUCELABS_ACCESS_KEY

// if accessKey is supplied as env variable, this is a remote Saucelabs test
var isRemoteTest = accessKey ? true : ''
var host = isRemoteTest ? 'http://uppy.io' : 'http://localhost:4000'

// FYI: old Chrome on Windows XP,
// Opera 12 on Linux — didn’t pass
var platforms = [
  // { browser: 'Opera', version: '12', os: 'Linux' },
  // { browser: 'iphone', version: '9.2', os: 'OS X 10.10' },
  { browser: 'Firefox', version: '34.0', os: 'Windows 7' },
  { browser: 'Chrome', version: '48.0', os: 'Windows XP' }
]

var tests = [
  require('./multipart.spec.js'),
  require('./i18n.spec.js'),
  require('./dragdrop.spec.js')
]

function buildDriver (platform) {
  var driver
  if (isRemoteTest) {
    driver = new webdriver
        .Builder()
        .withCapabilities({
          'browserName': platform.browser,
          'platform': platform.os,
          'version': platform.version,
          'username': username,
          'accessKey': accessKey
        })
        .usingServer('http://' + username + ':' + accessKey +
                    '@ondemand.saucelabs.com:80/wd/hub')
        .build()
    driver.setFileDetector(new remote.FileDetector())
  } else {
    driver = new webdriver
      .Builder()
      .forBrowser('firefox')
      .build()
  }
  return driver
}

if (isRemoteTest) {
  platforms.forEach(function (platform) {
    tests.forEach(function (test) {
      var driver = buildDriver(platform)
      test(driver, platform, host)
    })
  })
} else {
  tests.forEach(function (test) {
    var driver = buildDriver()
    test(driver, { browser: 'Firefox', version: 'Version', os: 'Local' }, host)
  })
}
<|endoftext|>


<commit_before>/* global module:false */
module.exports = function(grunt) {

  var fs, files, opts, builds = {};

  opts = {
    baseUrl: "src",
		name: "../build/REPLACE",
		out: "dist/REPLACE.js",
		mainConfigFile: "build/config.js"
  };

  fs = require( 'fs' );
  files = fs.readdirSync( "build/custom/" );

  files.forEach(function( file ) {
    if( /\.js$/.test(file) ){
      var name = file.replace(/\.js$/, "");

      builds[name] = {
        options: opts
      };

      builds[name].options.name = opts.name.replace("REPLACE", "custom/" + name );
      builds[name].options.out = opts.out.replace("REPLACE", name );
    }
  });

  builds.main = {
		options: {
			baseUrl: "src",
			name: "../build/main",
			out: "dist/main.js",
			mainConfigFile: "build/config.js"
		}
	};

	// Project configuration.
	grunt.initConfig({
		meta: {
			version: '0.1.0',
			banner: '/*! Shoestring - v<%= meta.version %> - ' +
				'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
				'* http://github.com/filamentgroup/shoestring/\n' +
				'* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
				'Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */ \n'
		},

		qunit: {
			files: ['test/unit/*.html']
		},

		requirejs: builds,

		// NOTE purely for the banner
		concat: {
			options: {
				banner: '<%= meta.banner %>',
				stripBanners: true
			},

			main: {
				src: ['dist/main.js'],
				dest: 'dist/shoestring.js'
			}
		},

		uglify: {
			all: {
				options: {
					banner: '<%= meta.banner %>',
					report: 'gzip'
				},

				files: {
					'dist/shoestring.min.js': ['dist/shoestring.js']
				}
			}
		},

		jshint: {
			all: {
				jshintrc: true,
				src: ['Gruntfile.js', 'src/shoestring.js', 'src/extensions/**/*.js']
			}
		}
	});

	// These plugins provide necessary tasks.
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-qunit');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-requirejs');

	// Default task.
	grunt.registerTask('build', 'requirejs concat uglify'.split(' ') );
	grunt.registerTask('test', 'jshint qunit'.split(' ') );
	grunt.registerTask('default', 'build test'.split(' ') );
};
<commit_msg>need to get build passing<commit_after>/* global module:false */
module.exports = function(grunt) {

  var fs, files, opts, builds = {};

  opts = {
    baseUrl: "src",
		name: "../build/REPLACE",
		out: "dist/REPLACE.js",
		mainConfigFile: "build/config.js"
  };

  fs = require( 'fs' );
  files = fs.readdirSync( "build/custom/" );

  files.forEach(function( file ) {
    if( /\.js$/.test(file) ){
      var name = file.replace(/\.js$/, "");

      builds[name] = {
        options: opts
      };

      builds[name].options.name = opts.name.replace("REPLACE", "custom/" + name );
      builds[name].options.out = opts.out.replace("REPLACE", name );
    }
  });

  builds.main = {
		options: {
			baseUrl: "src",
			name: "../build/main",
			out: "dist/main.js",
			mainConfigFile: "build/config.js"
		}
	};

	// Project configuration.
	grunt.initConfig({
		meta: {
			version: '0.1.0',
			banner: '/*! Shoestring - v<%= meta.version %> - ' +
				'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
				'* http://github.com/filamentgroup/shoestring/\n' +
				'* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
				'Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */ \n'
		},

		qunit: {
			files: ['test/unit/*.html']
		},

		requirejs: builds,

		// NOTE purely for the banner
		concat: {
			options: {
				banner: '<%= meta.banner %>',
				stripBanners: true
			},

			main: {
				src: ['dist/main.js'],
				dest: 'dist/shoestring.js'
			}
		},

		uglify: {
			all: {
				options: {
					banner: '<%= meta.banner %>',
					report: 'gzip'
				},

				files: {
					'dist/shoestring.min.js': ['dist/shoestring.js']
				}
			}
		},

		jshint: {
			all: {
				jshintrc: true,
				src: ['Gruntfile.js', 'src/shoestring.js', 'src/extensions/**/*.js']
			}
		}
	});

	// These plugins provide necessary tasks.
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-qunit');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-requirejs');

	// Default task.
	grunt.registerTask('build', 'requirejs concat uglify'.split(' ') );
	grunt.registerTask('test', 'qunit'.split(' ') );
	grunt.registerTask('default', 'build test'.split(' ') );
};
<|endoftext|>