<commit_before>// Generated on 2014-06-10 using generator-angular 0.9.0-0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  function connectMiddlewares (connect, options) {
    var middlewares = [];

    // enable API proxy
    middlewares.push(require('grunt-connect-proxy/lib/utils').proxyRequest);

    // enable Angular's HTML5 mode
    // based on http://stackoverflow.com/a/20553608/2486196
    middlewares.push(function (req, res, next) {
      var exists = false;
      var file = req.url.split('?')[0];

      for(var i = 0; i < options.base.length; i++) {
        if(require('fs').existsSync(options.base[i] + file)) {
          exists = true;
          break;
        }
      }

      if(!exists) {
        res.setHeader('Location', '/');
        req.url = '/';
      }

      next();
    });

    if (!Array.isArray(options.base)) {
      options.base = [options.base];
    }
    options.base.forEach(function(base) {
      // Serve static files.
      middlewares.push(connect.static(base));
    });

    return middlewares;
  }

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: require('./bower.json').appPath || 'app',
      dist: 'dist'
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/components/**/*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/*.html',
          '<%= yeoman.app %>/components/**/*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        middleware: connectMiddlewares,
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        },
        proxies: [
          {
            context: '/api',
            host: 'shownot.es',
            port: 80,
            https: false,
            xforward: false,
            headers: {
              'host': 'shownot.es'
            }
          }
        ]
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        exclude: [ /handlebars/, /fira/, /nunjucks/ ],
        ignorePath: new RegExp('^<%= yeoman.app %>/')
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: '<%= yeoman.app %>/bower_components/'
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/images/generated',
        imagesDir: '<%= yeoman.app %>/images',
        javascriptsDir: '<%= yeoman.app %>/scripts',
        fontsDir: '<%= yeoman.app %>/styles/fonts',
        importPath: '<%= yeoman.app %>/bower_components',
        httpImagesPath: '/images',
        httpGeneratedImagesPath: '/images/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%= yeoman.dist %>/images/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: ['<%= yeoman.app %>/*.html', '<%= yeoman.app %>/components/**/*.html'],
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/*.html', '<%= yeoman.dist %>/components/**/*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ngmin tries to make the code safe for minification automatically by
    // using the Angular long form for dependency injection. It doesn't work on
    // things like resolve or inject so those have to be done manually.
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    nunjucks: {
      osftemplates: {
        baseDir: '<%= yeoman.app %>/bower_components/osf-export-templates/tpls',
        src: '**/*.tpl',
        dest: '.tmp/scripts/osf-templates.js',
        options: {
          name: function(filename) {
            var format = filename.substr(0, filename.length - 4);
            return 'osf_' + format;
          }
        }
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'components/**/*.html',
            'languages/*.json',
            'images/{,*/}*.{webp}',
            'fonts/**/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          cwd: '<%= yeoman.app %>/bower_components/fira',
          dest: '<%= yeoman.dist %>/fonts/fira',
          expand: true,
          src: [ '**/*.{eot,otf,ttf,woff}', ]
        }]
      },
      fonts: {
        files: [{
          cwd: '<%= yeoman.app %>/bower_components/fira',
          dest: '.tmp/fonts/fira',
          expand: true,
          src: [ '**/*.{eot,otf,ttf,woff}', ]
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'copy:fonts',
      'nunjucks:osftemplates',
      'wiredep',
      'configureProxies:livereload',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'nunjucks:osftemplates',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<commit_msg>fix SVGs in dist<commit_after>// Generated on 2014-06-10 using generator-angular 0.9.0-0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  function connectMiddlewares (connect, options) {
    var middlewares = [];

    // enable API proxy
    middlewares.push(require('grunt-connect-proxy/lib/utils').proxyRequest);

    // enable Angular's HTML5 mode
    // based on http://stackoverflow.com/a/20553608/2486196
    middlewares.push(function (req, res, next) {
      var exists = false;
      var file = req.url.split('?')[0];

      for(var i = 0; i < options.base.length; i++) {
        if(require('fs').existsSync(options.base[i] + file)) {
          exists = true;
          break;
        }
      }

      if(!exists) {
        res.setHeader('Location', '/');
        req.url = '/';
      }

      next();
    });

    if (!Array.isArray(options.base)) {
      options.base = [options.base];
    }
    options.base.forEach(function(base) {
      // Serve static files.
      middlewares.push(connect.static(base));
    });

    return middlewares;
  }

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: require('./bower.json').appPath || 'app',
      dist: 'dist'
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/components/**/*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/*.html',
          '<%= yeoman.app %>/components/**/*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        middleware: connectMiddlewares,
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        },
        proxies: [
          {
            context: '/api',
            host: 'shownot.es',
            port: 80,
            https: false,
            xforward: false,
            headers: {
              'host': 'shownot.es'
            }
          }
        ]
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        exclude: [ /handlebars/, /fira/, /nunjucks/ ],
        ignorePath: new RegExp('^<%= yeoman.app %>/')
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: '<%= yeoman.app %>/bower_components/'
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/images/generated',
        imagesDir: '<%= yeoman.app %>/images',
        javascriptsDir: '<%= yeoman.app %>/scripts',
        fontsDir: '<%= yeoman.app %>/styles/fonts',
        importPath: '<%= yeoman.app %>/bower_components',
        httpImagesPath: '/images',
        httpGeneratedImagesPath: '/images/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%= yeoman.dist %>/images/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: ['<%= yeoman.app %>/*.html', '<%= yeoman.app %>/components/**/*.html'],
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/*.html', '<%= yeoman.dist %>/components/**/*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      options: {
        plugins: [
          // these break our SVGs..
          { convertPathData: false },
          { mergePaths: false }
        ]
      },
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ngmin tries to make the code safe for minification automatically by
    // using the Angular long form for dependency injection. It doesn't work on
    // things like resolve or inject so those have to be done manually.
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    nunjucks: {
      osftemplates: {
        baseDir: '<%= yeoman.app %>/bower_components/osf-export-templates/tpls',
        src: '**/*.tpl',
        dest: '.tmp/scripts/osf-templates.js',
        options: {
          name: function(filename) {
            var format = filename.substr(0, filename.length - 4);
            return 'osf_' + format;
          }
        }
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'components/**/*.html',
            'languages/*.json',
            'images/{,*/}*.{webp}',
            'fonts/**/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          cwd: '<%= yeoman.app %>/bower_components/fira',
          dest: '<%= yeoman.dist %>/fonts/fira',
          expand: true,
          src: [ '**/*.{eot,otf,ttf,woff}', ]
        }]
      },
      fonts: {
        files: [{
          cwd: '<%= yeoman.app %>/bower_components/fira',
          dest: '.tmp/fonts/fira',
          expand: true,
          src: [ '**/*.{eot,otf,ttf,woff}', ]
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'copy:fonts',
      'nunjucks:osftemplates',
      'wiredep',
      'configureProxies:livereload',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'nunjucks:osftemplates',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<|endoftext|>


<commit_before>'use strict';

const async = require('async'),
	EXEC_LIMIT = 10;

module.exports = function (grunt) {

	const iosSrc = [
		'iphone/Classes/*.h',
		'iphone/Classes/*.m',
		'iphone/Classes/Layout/*.h',
		'iphone/Classes/Layout/*.m',
		'iphone/TitaniumKit/TitaniumKit/*.h',
		'iphone/TitaniumKit/TitaniumKit/Sources/**/*.h',
		'iphone/TitaniumKit/TitaniumKit/Sources/**/*.m'
	];
	const androidSrc = [
		'android/build/src/**/*.java',
		'android/kroll-apt/src/**/*.java',
		'android/modules/*/src/**/*.java',
		'android/runtime/*/src/**/*.java',
		'android/titanium/src/**/*.java'
	];

	// Project configuration.
	grunt.initConfig({
		appcJs: {
			src: [
				'dangerfile.js',
				'Gruntfile.js',
				'apidoc/**/*.js',
				'build/**/*.js',
				'cli/!(locales)/**/*.js',
				'android/cli/!(locales)/**/*.js',
				'iphone/cli/!(locales)/**/*.js',
				'tests/Resources/**/*test.js'
			]
		},
		clangFormat: {
			android: { src: androidSrc },
			ios: { src: iosSrc }
		},
		checkFormat: {
			android: { src: androidSrc },
			ios: { src: iosSrc }
		}
	});

	grunt.registerTask('validate:docs', 'Validates the docs.', function () {
		const done = this.async();
		const fork = require('child_process').fork, // eslint-disable-line security/detect-child-process
			path = require('path'),
			apidoc = path.join(__dirname, 'apidoc');

		const validate = fork(path.join(apidoc, 'validate'), [], { cwd: apidoc, silent: true });
		let output = '';

		validate.stderr.on('data', function (data) {
			output += data;
		});

		validate.on('close', function (code) {
			if (code !== 0) {
				done(new Error('Docs validation failed with exit code: ' + code + '\nOutput: ' + output));
			}
			done();
		});
	});

	function validateFormatting() {
		const done = this.async(),
			clangFormat = require('clang-format');

		// Iterate over all specified file groups.
		let src = [];
		this.files.forEach(function (f) {
			// Concat specified files.
			src = src.concat(f.src.filter(function (filepath) {
				// Warn on and remove invalid source files (if nonull was set).
				if (!grunt.file.exists(filepath)) {
					grunt.log.warn('Source file "' + filepath + '" not found.');
					return false;
				} else {
					return true;
				}
			}));
		});

		// Check format of the files in parallel, but limit number of simultaneous execs or we'll fail
		const errors = [];
		async.mapLimit(src, EXEC_LIMIT, function (filepath, cb) {
			let stdout = '';

			const proc = clangFormat.spawnClangFormat([ '-style=file', '-output-replacements-xml', filepath ], function () {}, 'pipe');
			proc.stdout.on('data', function (data) {
				stdout += data.toString();
			});
			proc.on('close', function (exit) {
				if (exit) {
					grunt.log.warn('Exit code: ' + exit);
					grunt.fail.fatal(stdout);
					cb(exit);
				}

				const modified = stdout.replace(/\r?\n/g, '');
				if (modified !== '<?xml version=\'1.0\'?><replacements xml:space=\'preserve\' incomplete_format=\'false\'></replacements>') {
					// Record failure, because formatting is bad.
					// TODO Get the correctly formatted source? Give more details on the bad sections?
					errors.push(new Error('Formatting incorrect on "' + filepath + '", proposed changes: ' + stdout));
				}
				// grunt.log.ok(filepath);
				cb();
			});
		}, function () {
			if (errors.length > 0) {
				grunt.fail.fatal(errors.join('\n'));
				return done(new Error(errors.join('\n')));
			}
			done();
		});
	}

	grunt.registerMultiTask('checkFormat', 'Validates the source code formatting.', validateFormatting);

	// Load grunt plugins for modules
	grunt.loadNpmTasks('grunt-mocha-test');
	grunt.loadNpmTasks('grunt-appc-js');
	grunt.loadNpmTasks('grunt-clang-format');
	grunt.loadNpmTasks('grunt-contrib-clean');

	// linting: run eslint against js, standard appc checks, check ios/android format via clang, run doc validation script
	grunt.registerTask('lint', [ 'appcJs:src:lintOnly', 'checkFormat:ios', 'checkFormat:android', 'validate:docs' ]);

	// Tasks for formatting the source code according to our clang/eslint rules
	grunt.registerTask('format:js', [ 'appcJs:src:lint:fix' ]);
	grunt.registerTask('format:android', [ 'clangFormat:android' ]);
	grunt.registerTask('format:ios', [ 'clangFormat:ios' ]);
	grunt.registerTask('format', [ 'format:android', 'format:ios', 'format:js' ]);

	// By default, run linting
	grunt.registerTask('default', [ 'lint' ]);
};
<commit_msg>chore(lint): Add Android runtime JS files to lint listing<commit_after>'use strict';

const async = require('async'),
	EXEC_LIMIT = 10;

module.exports = function (grunt) {

	const iosSrc = [
		'iphone/Classes/*.h',
		'iphone/Classes/*.m',
		'iphone/Classes/Layout/*.h',
		'iphone/Classes/Layout/*.m',
		'iphone/TitaniumKit/TitaniumKit/*.h',
		'iphone/TitaniumKit/TitaniumKit/Sources/**/*.h',
		'iphone/TitaniumKit/TitaniumKit/Sources/**/*.m'
	];
	const androidSrc = [
		'android/build/src/**/*.java',
		'android/kroll-apt/src/**/*.java',
		'android/modules/*/src/**/*.java',
		'android/runtime/*/src/**/*.java',
		'android/titanium/src/**/*.java'
	];

	// Project configuration.
	grunt.initConfig({
		appcJs: {
			src: [
				'dangerfile.js',
				'Gruntfile.js',
				'apidoc/**/*.js',
				'build/**/*.js',
				'cli/!(locales)/**/*.js',
				'android/cli/!(locales)/**/*.js',
				'android/runtime/common/src/js/**/*.js',
				'iphone/cli/!(locales)/**/*.js',
				'tests/Resources/**/*test.js'
			]
		},
		clangFormat: {
			android: { src: androidSrc },
			ios: { src: iosSrc }
		},
		checkFormat: {
			android: { src: androidSrc },
			ios: { src: iosSrc }
		}
	});

	grunt.registerTask('validate:docs', 'Validates the docs.', function () {
		const done = this.async();
		const fork = require('child_process').fork, // eslint-disable-line security/detect-child-process
			path = require('path'),
			apidoc = path.join(__dirname, 'apidoc');

		const validate = fork(path.join(apidoc, 'validate'), [], { cwd: apidoc, silent: true });
		let output = '';

		validate.stderr.on('data', function (data) {
			output += data;
		});

		validate.on('close', function (code) {
			if (code !== 0) {
				done(new Error('Docs validation failed with exit code: ' + code + '\nOutput: ' + output));
			}
			done();
		});
	});

	function validateFormatting() {
		const done = this.async(),
			clangFormat = require('clang-format');

		// Iterate over all specified file groups.
		let src = [];
		this.files.forEach(function (f) {
			// Concat specified files.
			src = src.concat(f.src.filter(function (filepath) {
				// Warn on and remove invalid source files (if nonull was set).
				if (!grunt.file.exists(filepath)) {
					grunt.log.warn('Source file "' + filepath + '" not found.');
					return false;
				} else {
					return true;
				}
			}));
		});

		// Check format of the files in parallel, but limit number of simultaneous execs or we'll fail
		const errors = [];
		async.mapLimit(src, EXEC_LIMIT, function (filepath, cb) {
			let stdout = '';

			const proc = clangFormat.spawnClangFormat([ '-style=file', '-output-replacements-xml', filepath ], function () {}, 'pipe');
			proc.stdout.on('data', function (data) {
				stdout += data.toString();
			});
			proc.on('close', function (exit) {
				if (exit) {
					grunt.log.warn('Exit code: ' + exit);
					grunt.fail.fatal(stdout);
					cb(exit);
				}

				const modified = stdout.replace(/\r?\n/g, '');
				if (modified !== '<?xml version=\'1.0\'?><replacements xml:space=\'preserve\' incomplete_format=\'false\'></replacements>') {
					// Record failure, because formatting is bad.
					// TODO Get the correctly formatted source? Give more details on the bad sections?
					errors.push(new Error('Formatting incorrect on "' + filepath + '", proposed changes: ' + stdout));
				}
				// grunt.log.ok(filepath);
				cb();
			});
		}, function () {
			if (errors.length > 0) {
				grunt.fail.fatal(errors.join('\n'));
				return done(new Error(errors.join('\n')));
			}
			done();
		});
	}

	grunt.registerMultiTask('checkFormat', 'Validates the source code formatting.', validateFormatting);

	// Load grunt plugins for modules
	grunt.loadNpmTasks('grunt-mocha-test');
	grunt.loadNpmTasks('grunt-appc-js');
	grunt.loadNpmTasks('grunt-clang-format');
	grunt.loadNpmTasks('grunt-contrib-clean');

	// linting: run eslint against js, standard appc checks, check ios/android format via clang, run doc validation script
	grunt.registerTask('lint', [ 'appcJs:src:lintOnly', 'checkFormat:ios', 'checkFormat:android', 'validate:docs' ]);

	// Tasks for formatting the source code according to our clang/eslint rules
	grunt.registerTask('format:js', [ 'appcJs:src:lint:fix' ]);
	grunt.registerTask('format:android', [ 'clangFormat:android' ]);
	grunt.registerTask('format:ios', [ 'clangFormat:ios' ]);
	grunt.registerTask('format', [ 'format:android', 'format:ios', 'format:js' ]);

	// By default, run linting
	grunt.registerTask('default', [ 'lint' ]);
};
<|endoftext|>


<commit_before>import test from 'blue-tape';
import request from 'superagent';
import mocker from 'superagent-mocker';

import cli from '../src/cli';
import mockProcess from './process';

const mock = mocker(request);

const env = {
  TRAVIS: 'true',
  TRAVIS_REPO_SLUG: 'tester/project',
  TRAVIS_BRANCH: 'test',
  TRAVIS_PULL_REQUEST: 'false',
  DEPCHECK_TOKEN: 'project-token',
};

test('should post the input and succeed', assert => {
  assert.timeoutAfter(5000); // 5 seconds
  assert.plan(7);

  const processMocker = mockProcess({
    env,
    input: 'project-result',
  });

  mock.post('https://depcheck.tk/github/tester/project', req => {
    assert.deepEqual(req.body.token, 'project-token');
    assert.deepEqual(req.body.branch, 'test');
    assert.deepEqual(req.body.report, '');
    assert.deepEqual(req.body.result, 'project-result');
  });

  return cli(processMocker)
    .then(() => assert.equal(processMocker.exit.value, 0))
    .then(() => assert.equal(processMocker.stdout.value, 'Post web report succeed.'))
    .then(() => assert.equal(processMocker.stderr.value, ''))
    .catch(error => assert.fail(error))
    .then(() => mock.clearRoutes());
});

test('should log error and exit with -1 when request failed', assert => {
  assert.timeoutAfter(5000); // 5 seconds

  const processMocker = mockProcess({ env });

  mock.post('https://depcheck.tk/github/tester/project', () => {
    throw { // eslint-disable-line no-throw-literal
      response: {
        body: 'error-body',
      },
    };
  });

  return cli(processMocker)
    .then(() => assert.equal(processMocker.exit.value, -1))
    .then(() => assert.equal(processMocker.stdout.value, ''))
    .then(() => assert.equal(processMocker.stderr.value, "'error-body'"))
    .catch(error => assert.fail(error))
    .then(() => mock.clearRoutes());
});
<commit_msg>Add test cases about CLI arguments.<commit_after>import test from 'blue-tape';
import request from 'superagent';
import mocker from 'superagent-mocker';

import cli from '../src/cli';
import mockProcess from './process';

const mock = mocker(request);

const env = {
  TRAVIS: 'true',
  TRAVIS_REPO_SLUG: 'tester/project',
  TRAVIS_BRANCH: 'test',
  TRAVIS_PULL_REQUEST: 'false',
  DEPCHECK_TOKEN: 'project-token',
};

test('should post the input and succeed', assert => {
  assert.timeoutAfter(5000); // 5 seconds
  assert.plan(7);

  const processMocker = mockProcess({
    env,
    input: 'project-result',
  });

  mock.post('https://depcheck.tk/github/tester/project', req => {
    assert.deepEqual(req.body.token, 'project-token');
    assert.deepEqual(req.body.branch, 'test');
    assert.deepEqual(req.body.report, '');
    assert.deepEqual(req.body.result, 'project-result');
  });

  return cli(processMocker)
    .then(() => assert.equal(processMocker.exit.value, 0))
    .then(() => assert.equal(processMocker.stdout.value, 'Post web report succeed.'))
    .then(() => assert.equal(processMocker.stderr.value, ''))
    .catch(error => assert.fail(error))
    .then(() => mock.clearRoutes());
});

test('should log error and exit with -1 when request failed', assert => {
  assert.timeoutAfter(5000); // 5 seconds

  const processMocker = mockProcess({ env });

  mock.post('https://depcheck.tk/github/tester/project', () => {
    throw { // eslint-disable-line no-throw-literal
      response: {
        body: 'error-body',
      },
    };
  });

  return cli(processMocker)
    .then(() => assert.equal(processMocker.exit.value, -1))
    .then(() => assert.equal(processMocker.stdout.value, ''))
    .then(() => assert.equal(processMocker.stderr.value, "'error-body'"))
    .catch(error => assert.fail(error))
    .then(() => mock.clearRoutes());
});

test('should pass the first CLI argument as report name', assert => {
  assert.timeoutAfter(5000); // 5 seconds
  assert.plan(2);

  const processMocker = mockProcess({
    env,
    argv: ['report'],
  });

  mock.post('https://depcheck.tk/github/tester/project', req =>
    assert.equal(req.body.report, 'report'));

  return cli(processMocker)
    .then(() => assert.equal(processMocker.exit.value, 0))
    .catch(error => assert.fail(error))
    .then(() => mock.clearRoutes());
});

test('should pass the second CLI argument as service URL', assert => {
  assert.timeoutAfter(5000); // 5 seconds

  const processMocker = mockProcess({
    env,
    argv: [null, 'https://depcheck.service'],
  });

  mock.post('https://depcheck.service/github/tester/project', () => null);

  return cli(processMocker)
    .then(() => assert.equal(processMocker.exit.value, 0))
    .catch(error => assert.fail(error))
    .then(() => mock.clearRoutes());
});
<|endoftext|>


<commit_before>var gruntRequireConfig = require('./src/dev/requireConfig').gruntConfig;
var gruntWrapperRequireConfig = require('./src/dev/requireConfig').gruntWrapperConfig;
var gruntDebugConfig = require('./src/dev/requireConfig').gruntDebugConfig;
module.exports = function (grunt) {
    grunt.initConfig({
        requirejs: {
            core: {
                options: gruntRequireConfig
            },
            wrapper: {
                options: gruntWrapperRequireConfig
            },
            debug: {
                options: gruntDebugConfig
            }
        },
        concat: {
            css: {
                src: 'build/tmp/*.css',
                dest: 'build/tmp/razorflow.core.theme.css'
            }
        },
        jst: {
            compile: {
                options: {
                    prettify: true,
                    processName: function (filePath) {
                        var parts = filePath.split('/');
                        var fileName = parts[parts.length - 1];
                        return fileName.split('.')[0];
                    },
                    amd: true,
                    global: false,
                    requires: {
                        'vendor/lodash' : '_'
                    }
                },
                files: {
                    "src/js/generated/templates.js": ["src/templates/*.html"]
                }
            }
        },
        less: {
            development: {
                options: {
                    paths: ['src/less']
                },
                files: {
                    "build/tmp/razorflow.css": "src/less/razorflow.less",
                    "build/tmp/rftheme.default.css": "build/tmp/less/rftheme.default.less"
                }
            }
        },
        jshint: {
            options: {
                jshintrc: 'src/js/.jshintrc'
            },
            files: [
                "src/js/**/*.js",
                "!src/js/vendor/**/*.js",
                "!src/js/generated/templates.js",
                "!src/js/buildutils/**/*.js"
            ]
        },
        karma: {
            dev: {
                configFile: './karma.conf.js',
                singleRun: true
            }
        },
        lodash: {
            build: {
                dest: 'src/vendor/js/lodash.rf.js',
                options: {
                    include: ['extend', 'pluck', 'map', 'each', 'isNumber', 'isString', 'isNaN', 'isArray', 'isObject', 'find', 'defer', 'delay', 'max', 'min', 'sortBy', 'flatten', 'clone', 'cloneDeep', 'values', 'pick', 'reduce', 'filter', 'indexOf', 'keys', 'debounce'],
                }
            }
        },
        cssmin: {
            minify: {
                expand: true,
                cwd: 'build/tmp/',
                src: ['razorflow.core.theme.css'],
                dest: 'build/assets/css',
                ext: '.min.css'
            }
        },
        // Need to copy products.json
        copy: {
            localToWebRF: {
                files: [
                    {src: ["build/js/razorflow.min.js"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/css/razorflow.min.css"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/js/templates.js"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/js/rfDemos.js"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/img/exampleImgs/*.png"], dest: '../webrf/backend/static/transfer/',flatten: true },
                ]
            }
        },
        copyto: {
            srcToBuild: {
                files: [
                    {cwd: 'src/vendor/js/', src: ['jquery.min.js'], dest: 'build/assets/js/'},
                ]
            },
            packageToBuild: {
                files: [
                    {cwd: 'src/package/', src: ['**/*'], dest: 'build/package/'},
                ],
            },
            packageMinToBuild: {
                files: [
                    {cwd: 'src/packages/minified/', src: ['**/*'], dest: 'build/packages/minified/'}
                ]
            },
            packageSrcToBuild: {
                files: [
                    {cwd: 'src/packages/source/', src: ['**/*'], dest: 'build/packages/source/'}
                ]
            },
            assetsToPackage: {
                files: [
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/package/files/'},
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/package/dashboard_quickstart/'},
                ],
            },
            assetsToMinPackage: {
                files: [
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/minified/files/'},
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/minified/dashboard_quickstart/'},
                ],
            },
            assetsToSrcPackage: {
                files: [
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/source/files/'},
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/source/dashboard_quickstart/'},
                ],
            },
            srcToPackage: {
                files: [
                    {cwd: 'src/dev/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/dev/'},
                    {cwd: 'src/fonts/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/fonts/'},
                    {cwd: 'src/img/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/img/'},
                    {cwd: 'src/js/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/js/'},
                    {cwd: 'src/less/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/less/'},
                    {cwd: 'src/templates/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/templates/'},
                    {cwd: 'src/vendor/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/vendor/'},
                    {cwd: 'tools/src/grunt-tasks/', src: ['themeGen.js'], dest: 'build/packages/source/source/javascript/tools/grunt-tasks/'}
                ]
            },
            packageToRelease: {
                files: [
                    {cwd: 'src/package/', src: ['**'], dest: '../package/rf/javascript/'}
                ]
            },
            razorcharts: {
                files: [
                    {cwd: '../razorcharts/src/js', src:['**'], dest: 'src/js/'}
                ]
            },
            sprite: {
                files: [
                    {cwd: 'src/img/', src:['**'], dest: 'build/img/'},
                    {cwd: 'src/img/', src:['**'], dest: 'build/assets/img/'}
                ]
            },
            themebuilder: {
                files: [
                    {cwd: 'src/less/', src: ["theme.less", "mixins.less", "theme/variables.less"], dest: 'build/tmp/themebuilder/'},
                    {cwd: 'src/js/themebuilder/config/', src: ["defaulttheme.json"], dest: 'build/tmp/themebuilder/'}
                ]
            },
            connectors: {
                files: [
                    {cwd: '../cloudconnect/src/', src:['**'], dest: 'extras/connectors/'}
                ]
            }
        },
        replace: {
            removeAMD: {
                src: "build/assets/js/razorflow.min.js",
                overwrite: true,
                replacements: [
                    {from: /\bdefine\b/g, to: "_dfn"},
                    {from: /\brequire\b/g, to: "_rqr"}
                ]
            }
        },
        clean: {
            build: ["build"]
        },
        featuregen: {
            getFeature: {
                options: {
                    files: "src/js/**/*.js",
                    out: "build/out.html",
                    extension: ".js"
                }
            }
        },
        themegen: {
            defaultTheme: {
                options: {
                    themeJSON: "src/js/themebuilder/config/defaulttheme.json",
                    out: "build/tmp/less/rftheme.default.less"
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-requirejs');
    // grunt.loadNpmTasks('grunt-contrib-jst');
    grunt.loadTasks("../grunt-contrib-jst/tasks");
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-karma');
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-lodash');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-text-replace');
    grunt.loadNpmTasks('grunt-copy-to');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadTasks("./tools/src/grunt-tasks");

    grunt.registerTask('compile', ['themegen', 'less:development', 'jst:compile', 'copyto:sprite', 'copyto:themebuilder']);
    grunt.registerTask('test', ['compile', 'karma:dev', 'shell:coverageReport']);

    grunt.registerTask('build', ["clean:build", "copyto:razorcharts", "compile", "less", "jst:compile", 'requirejs:core', 'requirejs:wrapper', 'requirejs:debug', "replace:removeAMD", 'concat:css', 'cssmin:minify', "copyto:srcToBuild"])
    grunt.registerTask('package', ['build', 'copyto:packageToBuild', 'copyto:assetsToPackage']);
    grunt.registerTask('packageMin', ['build', 'copyto:packageMinToBuild', 'copyto:assetsToMinPackage']);
    grunt.registerTask('packageSrc', ['build', 'copyto:packageSrcToBuild', 'copyto:assetsToSrcPackage', 'copyto:srcToPackage']);
    // grunt.registerTask('websiteRelease', ['build', 'cssmin:minify', 'squashdemos', "screenshotGen:examples", 'copy:localToWebRF'])
}<commit_msg>Added a main package task to grunt which creates both minified and source packages<commit_after>var gruntRequireConfig = require('./src/dev/requireConfig').gruntConfig;
var gruntWrapperRequireConfig = require('./src/dev/requireConfig').gruntWrapperConfig;
var gruntDebugConfig = require('./src/dev/requireConfig').gruntDebugConfig;
module.exports = function (grunt) {
    grunt.initConfig({
        requirejs: {
            core: {
                options: gruntRequireConfig
            },
            wrapper: {
                options: gruntWrapperRequireConfig
            },
            debug: {
                options: gruntDebugConfig
            }
        },
        concat: {
            css: {
                src: 'build/tmp/*.css',
                dest: 'build/tmp/razorflow.core.theme.css'
            }
        },
        jst: {
            compile: {
                options: {
                    prettify: true,
                    processName: function (filePath) {
                        var parts = filePath.split('/');
                        var fileName = parts[parts.length - 1];
                        return fileName.split('.')[0];
                    },
                    amd: true,
                    global: false,
                    requires: {
                        'vendor/lodash' : '_'
                    }
                },
                files: {
                    "src/js/generated/templates.js": ["src/templates/*.html"]
                }
            }
        },
        less: {
            development: {
                options: {
                    paths: ['src/less']
                },
                files: {
                    "build/tmp/razorflow.css": "src/less/razorflow.less",
                    "build/tmp/rftheme.default.css": "build/tmp/less/rftheme.default.less"
                }
            }
        },
        jshint: {
            options: {
                jshintrc: 'src/js/.jshintrc'
            },
            files: [
                "src/js/**/*.js",
                "!src/js/vendor/**/*.js",
                "!src/js/generated/templates.js",
                "!src/js/buildutils/**/*.js"
            ]
        },
        karma: {
            dev: {
                configFile: './karma.conf.js',
                singleRun: true
            }
        },
        lodash: {
            build: {
                dest: 'src/vendor/js/lodash.rf.js',
                options: {
                    include: ['extend', 'pluck', 'map', 'each', 'isNumber', 'isString', 'isNaN', 'isArray', 'isObject', 'find', 'defer', 'delay', 'max', 'min', 'sortBy', 'flatten', 'clone', 'cloneDeep', 'values', 'pick', 'reduce', 'filter', 'indexOf', 'keys', 'debounce'],
                }
            }
        },
        cssmin: {
            minify: {
                expand: true,
                cwd: 'build/tmp/',
                src: ['razorflow.core.theme.css'],
                dest: 'build/assets/css',
                ext: '.min.css'
            }
        },
        // Need to copy products.json
        copy: {
            localToWebRF: {
                files: [
                    {src: ["build/js/razorflow.min.js"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/css/razorflow.min.css"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/js/templates.js"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/js/rfDemos.js"], dest: '../webrf/backend/static/transfer/', flatten:true},
                    {src: ["build/img/exampleImgs/*.png"], dest: '../webrf/backend/static/transfer/',flatten: true },
                ]
            }
        },
        copyto: {
            srcToBuild: {
                files: [
                    {cwd: 'src/vendor/js/', src: ['jquery.min.js'], dest: 'build/assets/js/'},
                ]
            },
            packageToBuild: {
                files: [
                    {cwd: 'src/package/', src: ['**/*'], dest: 'build/package/'},
                ],
            },
            packageMinToBuild: {
                files: [
                    {cwd: 'src/packages/minified/', src: ['**/*'], dest: 'build/packages/minified/'}
                ]
            },
            packageSrcToBuild: {
                files: [
                    {cwd: 'src/packages/source/', src: ['**/*'], dest: 'build/packages/source/'}
                ]
            },
            assetsToPackage: {
                files: [
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/package/files/'},
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/package/dashboard_quickstart/'},
                ],
            },
            assetsToMinPackage: {
                files: [
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/minified/files/'},
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/minified/dashboard_quickstart/'},
                ],
            },
            assetsToSrcPackage: {
                files: [
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/source/files/'},
                    {cwd: 'build/assets/', src: ["js/**", "css/**", "img/**"], dest: 'build/packages/source/dashboard_quickstart/'},
                ],
            },
            srcToPackage: {
                files: [
                    {cwd: 'src/dev/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/dev/'},
                    {cwd: 'src/fonts/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/fonts/'},
                    {cwd: 'src/img/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/img/'},
                    {cwd: 'src/js/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/js/'},
                    {cwd: 'src/less/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/less/'},
                    {cwd: 'src/templates/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/templates/'},
                    {cwd: 'src/vendor/', src: ['**/*'], dest: 'build/packages/source/source/javascript/src/vendor/'},
                    {cwd: 'tools/src/grunt-tasks/', src: ['themeGen.js'], dest: 'build/packages/source/source/javascript/tools/grunt-tasks/'}
                ]
            },
            packageToRelease: {
                files: [
                    {cwd: 'src/package/', src: ['**'], dest: '../package/rf/javascript/'}
                ]
            },
            razorcharts: {
                files: [
                    {cwd: '../razorcharts/src/js', src:['**'], dest: 'src/js/'}
                ]
            },
            sprite: {
                files: [
                    {cwd: 'src/img/', src:['**'], dest: 'build/img/'},
                    {cwd: 'src/img/', src:['**'], dest: 'build/assets/img/'}
                ]
            },
            themebuilder: {
                files: [
                    {cwd: 'src/less/', src: ["theme.less", "mixins.less", "theme/variables.less"], dest: 'build/tmp/themebuilder/'},
                    {cwd: 'src/js/themebuilder/config/', src: ["defaulttheme.json"], dest: 'build/tmp/themebuilder/'}
                ]
            },
            connectors: {
                files: [
                    {cwd: '../cloudconnect/src/', src:['**'], dest: 'extras/connectors/'}
                ]
            }
        },
        replace: {
            removeAMD: {
                src: "build/assets/js/razorflow.min.js",
                overwrite: true,
                replacements: [
                    {from: /\bdefine\b/g, to: "_dfn"},
                    {from: /\brequire\b/g, to: "_rqr"}
                ]
            }
        },
        clean: {
            build: ["build"]
        },
        featuregen: {
            getFeature: {
                options: {
                    files: "src/js/**/*.js",
                    out: "build/out.html",
                    extension: ".js"
                }
            }
        },
        themegen: {
            defaultTheme: {
                options: {
                    themeJSON: "src/js/themebuilder/config/defaulttheme.json",
                    out: "build/tmp/less/rftheme.default.less"
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-requirejs');
    // grunt.loadNpmTasks('grunt-contrib-jst');
    grunt.loadTasks("../grunt-contrib-jst/tasks");
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-karma');
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-lodash');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-text-replace');
    grunt.loadNpmTasks('grunt-copy-to');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadTasks("./tools/src/grunt-tasks");

    grunt.registerTask('compile', ['themegen', 'less:development', 'jst:compile', 'copyto:sprite', 'copyto:themebuilder']);
    grunt.registerTask('test', ['compile', 'karma:dev', 'shell:coverageReport']);

    grunt.registerTask('build', ["clean:build", "copyto:razorcharts", "compile", "less", "jst:compile", 'requirejs:core', 'requirejs:wrapper', 'requirejs:debug', "replace:removeAMD", 'concat:css', 'cssmin:minify', "copyto:srcToBuild"])
    // grunt.registerTask('package', ['build', 'copyto:packageToBuild', 'copyto:assetsToPackage']);
    grunt.registerTask('packageMin', ['build', 'copyto:packageMinToBuild', 'copyto:assetsToMinPackage']);
    grunt.registerTask('packageSrc', ['build', 'copyto:packageSrcToBuild', 'copyto:assetsToSrcPackage', 'copyto:srcToPackage']);
    // grunt.registerTask('websiteRelease', ['build', 'cssmin:minify', 'squashdemos', "screenshotGen:examples", 'copy:localToWebRF'])
    grunt.registerTask('package', ['clean:build', 'build', 'copyto:packageMinToBuild', 'copyto:assetsToMinPackage', 'copyto:packageSrcToBuild', 'copyto:assetsToSrcPackage', 'copyto:srcToPackage']);
}<|endoftext|>


<commit_before>/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react'
import PropTypes from 'prop-types'
import uuid from 'uuid/v4'
import moment from 'moment'
import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
import Button from '@material-ui/core/Button'
import MoneyRaised from 'js/components/MoneyRaised/MoneyRaisedContainer'
import UserBackgroundImage from 'js/components/User/UserBackgroundImageContainer'
import UserMenu from 'js/components/User/UserMenuContainer'
import WidgetsContainer from 'js/components/Widget/WidgetsContainer'
import CampaignBase from 'js/components/Campaign/CampaignBase'
import Ad from 'js/components/Ad/Ad'
import LogTab from 'js/components/Dashboard/LogTabContainer'
import LogRevenue from 'js/components/Dashboard/LogRevenueContainer'
import LogConsentData from 'js/components/Dashboard/LogConsentDataContainer'
import LogAccountCreation from 'js/components/Dashboard/LogAccountCreationContainer'
import AssignExperimentGroups from 'js/components/Dashboard/AssignExperimentGroupsContainer'
import CircleIcon from 'material-ui/svg-icons/image/lens'
import HeartIcon from 'material-ui/svg-icons/action/favorite'
import {
  primaryColor,
  dashboardIconInactiveColor,
  dashboardIconActiveColor
} from 'js/theme/default'
import FadeInDashboardAnimation from 'js/components/General/FadeInDashboardAnimation'
import ErrorMessage from 'js/components/General/ErrorMessage'
import Fireworks from 'lib/fireworks-react'
import RaisedButton from 'material-ui/RaisedButton'
import CloseIcon from 'material-ui/svg-icons/navigation/close'
import NewUserTour from 'js/components/Dashboard/NewUserTourContainer'
import Notification from 'js/components/Dashboard/NotificationComponent'
import { getCurrentUser } from 'js/authentication/user'
import localStorageMgr from 'js/utils/localstorage-mgr'
import {
  setUserDismissedAdExplanation
} from 'js/utils/local-user-data-mgr'
import { STORAGE_NEW_USER_HAS_COMPLETED_TOUR } from 'js/constants'
import {
  goTo,
  loginURL
} from 'js/navigation/navigation'
import {
  getNumberOfAdsToShow,
  shouldShowAdExplanation,
  VERTICAL_AD_SLOT_DOM_ID,
  SECOND_VERTICAL_AD_SLOT_DOM_ID,
  HORIZONTAL_AD_SLOT_DOM_ID
} from 'js/ads/adSettings'

class Dashboard extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      errorMessage: null,
      tabId: uuid(),
      showFireworks: false,
      isUserAnonymous: false, // Set after mount if true
      // This may be false if the user cleared their storage,
      // which is why we only show the tour to recently-joined
      // users.
      userAlreadyViewedNewUserTour: localStorageMgr.getItem(STORAGE_NEW_USER_HAS_COMPLETED_TOUR) === 'true',
      numAdsToShow: getNumberOfAdsToShow(),
      showAdExplanation: shouldShowAdExplanation()
    }
  }

  componentDidMount () {
    this.determineAnonymousStatus()
  }

  /**
   * Check if the user is anonymous and update state with the
   * anonymous status.
   * @return {Promise<undefined>} A Promise that resolves after
   *   the state has been updated.
   */
  async determineAnonymousStatus () {
    const currentUser = await getCurrentUser()
    const isAnon = currentUser && currentUser.isAnonymous
    return new Promise((resolve, reject) => {
      this.setState({
        isUserAnonymous: isAnon
      }, () => {
        resolve()
      })
    })
  }

  showError (msg) {
    this.setState({
      errorMessage: msg
    })
  }

  clearError () {
    this.showError(null)
  }

  launchFireworks (show) {
    this.setState({
      showFireworks: show
    })
  }

  render () {
    // Props will be null on first render.
    const { user, app } = this.props
    const { tabId } = this.state
    const errorMessage = this.state.errorMessage

    // Whether or not a campaign should show on the dashboard
    // TODO: also make sure the user hasn't dismissed the campaign (`isCampaignShown` var)
    const isGlobalCampaignLive = !!((app && app.isGlobalCampaignLive))

    // Show the tour if the user joined recently and localStorage
    // does not have a flag marking the tour as already viewed.
    const showNewUserTour = (
      user &&
      moment().utc().diff(moment(user.joined), 'hours') < 2 &&
      !this.state.userAlreadyViewedNewUserTour
    )

    // Whether to show a global announcement.
    const showNotification = true

    return (

        <UserBackgroundImage user={user} showError={this.showError.bind(this)} />
        { user
          ? (
            <FadeInDashboardAnimation>

                <MoneyRaised
                  app={app}
                  style={{
                    fontSize: 24
                  }}
                  launchFireworks={this.launchFireworks.bind(this)}
                />
                <CircleIcon
                  color={dashboardIconInactiveColor}
                  hoverColor={dashboardIconActiveColor}
                  style={{
                    alignSelf: 'center',
                    width: 5,
                    height: 5,
                    marginTop: 2,
                    marginLeft: 12,
                    marginRight: 12
                  }}
                />
                <UserMenu
                  app={app}
                  user={user}
                  style={{
                    fontSize: 24
                  }}
                  isUserAnonymous={this.state.isUserAnonymous}
                />

            </FadeInDashboardAnimation>
          )
          : null
        }
        { showNotification
          ? (
            <Notification
              title={'Message Title'}
              message={`Here is some additional information about why we're notifying you.`}
              buttonText={'Click Me'}
              buttonAction={'http://example.com/some-link/'}
              // TODO: move into user menu div
              style={{
                zIndex: 11,
                position: 'absolute',
                right: 10,
                top: 50
              }}
            />
          )
          : null
        }
        { (this.state.isUserAnonymous && user)
          ? (
            <FadeInDashboardAnimation>

                <Paper>

                    <Typography variant={'body2'}>
                      Sign in to save your progress!
                    </Typography>
                    <Button
                      color={'primary'}
                      style={{
                        marginLeft: 10,
                        marginRight: 10
                      }}
                      onClick={() => {
                        goTo(loginURL, { noredirect: 'true' })
                      }}
                    >
                      Sign In
                    </Button>

                </Paper>

            </FadeInDashboardAnimation>
          )
          : null
        }
        <WidgetsContainer
          user={user}
          isCampaignLive={isGlobalCampaignLive}
          showError={this.showError.bind(this)}
        />
        { isGlobalCampaignLive
          ? (
            <FadeInDashboardAnimation>
              <CampaignBase
                showError={this.showError.bind(this)}
              />
            </FadeInDashboardAnimation>
          )
          : null
        }
        {
          this.state.showFireworks ? (

              <FadeInDashboardAnimation>
                <CloseIcon
                  style={{
                    position: 'absolute',
                    top: 14,
                    right: 14,
                    zIndex: 22,
                    height: 32,
                    width: 32,
                    color: '#FFF',
                    cursor: 'pointer'
                  }}
                  onClick={() => { this.launchFireworks(false) }}
                />
              </FadeInDashboardAnimation>
              <FadeInDashboardAnimation>

                    Tabbers, together we've raised over half a million dollars for charity. Congrats, and thank you!

                    <RaisedButton
                      label='Share This Milestone'
                      style={{
                        marginTop: 8
                      }}
                      primary
                    />

              </FadeInDashboardAnimation>
              <Fireworks
                width={window.innerWidth}
                height={window.innerHeight}
                background={'rgba(0, 0, 0, 0.01)'}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  zIndex: 20
                }}
              />

          )
            : null
        }
        { showNewUserTour ? <NewUserTour user={user} /> : null }

            {
              this.state.numAdsToShow > 2
                ? <Ad
                  adId={SECOND_VERTICAL_AD_SLOT_DOM_ID}
                  style={{
                    display: 'flex',
                    minWidth: 300,
                    overflow: 'visible'
                  }}
                />
                : null
            }
            {
              this.state.numAdsToShow > 1
                ? <Ad
                  adId={VERTICAL_AD_SLOT_DOM_ID}
                  style={{
                    display: 'flex',
                    minWidth: 300,
                    overflow: 'visible',
                    marginTop: 10
                  }}
                />
                : null
            }

          {
            this.state.numAdsToShow > 0
              ? (

                  { (this.state.showAdExplanation && user)
                    ? (
                      <FadeInDashboardAnimation>

                          <Paper>

                              <HeartIcon
                                color={primaryColor}
                                style={{
                                  width: 24,
                                  height: 24,
                                  marginRight: 14
                                }}
                              />
                              <Typography variant={'body2'}>
                                Did you know? The ads here are raising money for charity.
                              </Typography>
                              <Button
                                color={'primary'}
                                style={{
                                  marginLeft: 20,
                                  marginRight: 10
                                }}
                                onClick={() => {
                                  setUserDismissedAdExplanation()
                                  this.setState({
                                    showAdExplanation: false
                                  })
                                }}
                              >
                              Got it
                              </Button>

                          </Paper>

                      </FadeInDashboardAnimation>
                    )
                    : null
                  }
                  <Ad
                    adId={HORIZONTAL_AD_SLOT_DOM_ID}
                    style={{
                      overflow: 'visible',
                      minWidth: 728
                    }}
                  />

              ) : null
          }

        { user && tabId ? <LogTab user={user} tabId={tabId} /> : null }
        { user && tabId ? <LogRevenue user={user} tabId={tabId} /> : null }
        { user ? <LogConsentData user={user} /> : null }
        { user ? <LogAccountCreation user={user} /> : null }
        { user ? <AssignExperimentGroups user={user} isNewUser={false} /> : null }
        { errorMessage
          ? <ErrorMessage message={errorMessage}
            onRequestClose={this.clearError.bind(this)} />
          : null }

    )
  }
}

Dashboard.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string.isRequired,
    joined: PropTypes.string.isRequired
  }),
  app: PropTypes.shape({
    isGlobalCampaignLive: PropTypes.bool
  })
}

Dashboard.defaultProps = {
  app: {
    isGlobalCampaignLive: false
  }
}

export default Dashboard
<commit_msg>Move notification below the top-right user menu<commit_after>/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react'
import PropTypes from 'prop-types'
import uuid from 'uuid/v4'
import moment from 'moment'
import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
import Button from '@material-ui/core/Button'
import MoneyRaised from 'js/components/MoneyRaised/MoneyRaisedContainer'
import UserBackgroundImage from 'js/components/User/UserBackgroundImageContainer'
import UserMenu from 'js/components/User/UserMenuContainer'
import WidgetsContainer from 'js/components/Widget/WidgetsContainer'
import CampaignBase from 'js/components/Campaign/CampaignBase'
import Ad from 'js/components/Ad/Ad'
import LogTab from 'js/components/Dashboard/LogTabContainer'
import LogRevenue from 'js/components/Dashboard/LogRevenueContainer'
import LogConsentData from 'js/components/Dashboard/LogConsentDataContainer'
import LogAccountCreation from 'js/components/Dashboard/LogAccountCreationContainer'
import AssignExperimentGroups from 'js/components/Dashboard/AssignExperimentGroupsContainer'
import CircleIcon from 'material-ui/svg-icons/image/lens'
import HeartIcon from 'material-ui/svg-icons/action/favorite'
import {
  primaryColor,
  dashboardIconInactiveColor,
  dashboardIconActiveColor
} from 'js/theme/default'
import FadeInDashboardAnimation from 'js/components/General/FadeInDashboardAnimation'
import ErrorMessage from 'js/components/General/ErrorMessage'
import Fireworks from 'lib/fireworks-react'
import RaisedButton from 'material-ui/RaisedButton'
import CloseIcon from 'material-ui/svg-icons/navigation/close'
import NewUserTour from 'js/components/Dashboard/NewUserTourContainer'
import Notification from 'js/components/Dashboard/NotificationComponent'
import { getCurrentUser } from 'js/authentication/user'
import localStorageMgr from 'js/utils/localstorage-mgr'
import {
  setUserDismissedAdExplanation
} from 'js/utils/local-user-data-mgr'
import { STORAGE_NEW_USER_HAS_COMPLETED_TOUR } from 'js/constants'
import {
  goTo,
  loginURL
} from 'js/navigation/navigation'
import {
  getNumberOfAdsToShow,
  shouldShowAdExplanation,
  VERTICAL_AD_SLOT_DOM_ID,
  SECOND_VERTICAL_AD_SLOT_DOM_ID,
  HORIZONTAL_AD_SLOT_DOM_ID
} from 'js/ads/adSettings'

class Dashboard extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      errorMessage: null,
      tabId: uuid(),
      showFireworks: false,
      isUserAnonymous: false, // Set after mount if true
      // This may be false if the user cleared their storage,
      // which is why we only show the tour to recently-joined
      // users.
      userAlreadyViewedNewUserTour: localStorageMgr.getItem(STORAGE_NEW_USER_HAS_COMPLETED_TOUR) === 'true',
      numAdsToShow: getNumberOfAdsToShow(),
      showAdExplanation: shouldShowAdExplanation()
    }
  }

  componentDidMount () {
    this.determineAnonymousStatus()
  }

  /**
   * Check if the user is anonymous and update state with the
   * anonymous status.
   * @return {Promise<undefined>} A Promise that resolves after
   *   the state has been updated.
   */
  async determineAnonymousStatus () {
    const currentUser = await getCurrentUser()
    const isAnon = currentUser && currentUser.isAnonymous
    return new Promise((resolve, reject) => {
      this.setState({
        isUserAnonymous: isAnon
      }, () => {
        resolve()
      })
    })
  }

  showError (msg) {
    this.setState({
      errorMessage: msg
    })
  }

  clearError () {
    this.showError(null)
  }

  launchFireworks (show) {
    this.setState({
      showFireworks: show
    })
  }

  render () {
    // Props will be null on first render.
    const { user, app } = this.props
    const { tabId } = this.state
    const errorMessage = this.state.errorMessage

    // Whether or not a campaign should show on the dashboard
    // TODO: also make sure the user hasn't dismissed the campaign (`isCampaignShown` var)
    const isGlobalCampaignLive = !!((app && app.isGlobalCampaignLive))

    // Show the tour if the user joined recently and localStorage
    // does not have a flag marking the tour as already viewed.
    const showNewUserTour = (
      user &&
      moment().utc().diff(moment(user.joined), 'hours') < 2 &&
      !this.state.userAlreadyViewedNewUserTour
    )

    // Whether to show a global announcement.
    const showNotification = true

    return (

        <UserBackgroundImage user={user} showError={this.showError.bind(this)} />
        { user
          ? (
            <FadeInDashboardAnimation>

                  <MoneyRaised
                    app={app}
                    style={{
                      fontSize: 24
                    }}
                    launchFireworks={this.launchFireworks.bind(this)}
                  />
                  <CircleIcon
                    color={dashboardIconInactiveColor}
                    hoverColor={dashboardIconActiveColor}
                    style={{
                      alignSelf: 'center',
                      width: 5,
                      height: 5,
                      marginTop: 2,
                      marginLeft: 12,
                      marginRight: 12
                    }}
                  />
                  <UserMenu
                    app={app}
                    user={user}
                    style={{
                      fontSize: 24
                    }}
                    isUserAnonymous={this.state.isUserAnonymous}
                  />

                { showNotification
                  ? (
                    <Notification
                      title={'Message Title'}
                      message={`Here is some additional information about why we're notifying you.`}
                      buttonText={'Click Me'}
                      buttonAction={'http://example.com/some-link/'}
                      style={{}}
                    />
                  )
                  : null
                }

            </FadeInDashboardAnimation>
          )
          : null
        }
        { (this.state.isUserAnonymous && user)
          ? (
            <FadeInDashboardAnimation>

                <Paper>

                    <Typography variant={'body2'}>
                      Sign in to save your progress!
                    </Typography>
                    <Button
                      color={'primary'}
                      style={{
                        marginLeft: 10,
                        marginRight: 10
                      }}
                      onClick={() => {
                        goTo(loginURL, { noredirect: 'true' })
                      }}
                    >
                      Sign In
                    </Button>

                </Paper>

            </FadeInDashboardAnimation>
          )
          : null
        }
        <WidgetsContainer
          user={user}
          isCampaignLive={isGlobalCampaignLive}
          showError={this.showError.bind(this)}
        />
        { isGlobalCampaignLive
          ? (
            <FadeInDashboardAnimation>
              <CampaignBase
                showError={this.showError.bind(this)}
              />
            </FadeInDashboardAnimation>
          )
          : null
        }
        {
          this.state.showFireworks ? (

              <FadeInDashboardAnimation>
                <CloseIcon
                  style={{
                    position: 'absolute',
                    top: 14,
                    right: 14,
                    zIndex: 22,
                    height: 32,
                    width: 32,
                    color: '#FFF',
                    cursor: 'pointer'
                  }}
                  onClick={() => { this.launchFireworks(false) }}
                />
              </FadeInDashboardAnimation>
              <FadeInDashboardAnimation>

                    Tabbers, together we've raised over half a million dollars for charity. Congrats, and thank you!

                    <RaisedButton
                      label='Share This Milestone'
                      style={{
                        marginTop: 8
                      }}
                      primary
                    />

              </FadeInDashboardAnimation>
              <Fireworks
                width={window.innerWidth}
                height={window.innerHeight}
                background={'rgba(0, 0, 0, 0.01)'}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  zIndex: 20
                }}
              />

          )
            : null
        }
        { showNewUserTour ? <NewUserTour user={user} /> : null }

            {
              this.state.numAdsToShow > 2
                ? <Ad
                  adId={SECOND_VERTICAL_AD_SLOT_DOM_ID}
                  style={{
                    display: 'flex',
                    minWidth: 300,
                    overflow: 'visible'
                  }}
                />
                : null
            }
            {
              this.state.numAdsToShow > 1
                ? <Ad
                  adId={VERTICAL_AD_SLOT_DOM_ID}
                  style={{
                    display: 'flex',
                    minWidth: 300,
                    overflow: 'visible',
                    marginTop: 10
                  }}
                />
                : null
            }

          {
            this.state.numAdsToShow > 0
              ? (

                  { (this.state.showAdExplanation && user)
                    ? (
                      <FadeInDashboardAnimation>

                          <Paper>

                              <HeartIcon
                                color={primaryColor}
                                style={{
                                  width: 24,
                                  height: 24,
                                  marginRight: 14
                                }}
                              />
                              <Typography variant={'body2'}>
                                Did you know? The ads here are raising money for charity.
                              </Typography>
                              <Button
                                color={'primary'}
                                style={{
                                  marginLeft: 20,
                                  marginRight: 10
                                }}
                                onClick={() => {
                                  setUserDismissedAdExplanation()
                                  this.setState({
                                    showAdExplanation: false
                                  })
                                }}
                              >
                              Got it
                              </Button>

                          </Paper>

                      </FadeInDashboardAnimation>
                    )
                    : null
                  }
                  <Ad
                    adId={HORIZONTAL_AD_SLOT_DOM_ID}
                    style={{
                      overflow: 'visible',
                      minWidth: 728
                    }}
                  />

              ) : null
          }

        { user && tabId ? <LogTab user={user} tabId={tabId} /> : null }
        { user && tabId ? <LogRevenue user={user} tabId={tabId} /> : null }
        { user ? <LogConsentData user={user} /> : null }
        { user ? <LogAccountCreation user={user} /> : null }
        { user ? <AssignExperimentGroups user={user} isNewUser={false} /> : null }
        { errorMessage
          ? <ErrorMessage message={errorMessage}
            onRequestClose={this.clearError.bind(this)} />
          : null }

    )
  }
}

Dashboard.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string.isRequired,
    joined: PropTypes.string.isRequired
  }),
  app: PropTypes.shape({
    isGlobalCampaignLive: PropTypes.bool
  })
}

Dashboard.defaultProps = {
  app: {
    isGlobalCampaignLive: false
  }
}

export default Dashboard
<|endoftext|>