<commit_before>// Generated on 2014-05-01 using generator-angular 0.8.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'
//
var untildify = require('untildify');

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);

  // Define the configuration for all the tasks
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    hostname: process.env.SSO_DEV_HOST || 'localhost',
    port: process.env.SSO_DEV_PORT || 9000,

    // 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: ['bowerInstall']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all','karma:liveunit:run'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma:liveunit:run']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{less,css}'],
        tasks: ['less','autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      html: {
        files: ['<%= yeoman.app %>/{,*/}*.html'],
        tasks: ['includes:html']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: '<%=port %>',
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '0.0.0.0',
        livereload: 35729
      },
      livereload: {
        options: {
          open: 'http://<%=hostname %>:<%=port %>',
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= yeoman.dist %>'
        }
      }
    },

    less: {
      development:{
        files: {
          '.tmp/styles/main.css':'<%= yeoman.app %>/styles/main.less'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        '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 3 versions']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    bowerInstall: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath: '<%= yeoman.app %>/'
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          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: {
      dist:{
        src: '<%= yeoman.app %>/index.html',
        options: {
          dest: '<%= yeoman.dist %>',
          flow: {
            html: {
              steps: {
                js: ['concat', 'uglifyjs'],
                css: ['cssmin']
              },
              post: {}
            }
          }
        }
      },
      debug:{
        src: '<%= yeoman.app %>/index.html',
        options: {
          dest: '<%= yeoman.dist %>',
          flow: {
            html: {
              steps: {
                js: ['concat'],
                css: ['cssmin']
              },
              post: {}
            }
          }
        }
      }
    },

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

    // The following *-min tasks produce minified files in the dist folder
    cssmin: {
      options: {
        root: '<%= yeoman.app %>'
      }
    },

    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,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.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']
      }
    },

    // 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}',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        },{
          expand: true,
          cwd: '.tmp',
          dest: '<%= yeoman.dist %>',
          src: ['*.html']
        },{
          expand: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: ['error.html']
        }]
      },
      less: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.{less}'
      },
      debug: {
        expand: true,
        cwd: '.tmp/concat/scripts',
        dest: '<%= yeoman.dist %>/scripts',
        src: '{,*/}*.js'
      },
      dest:{
        cwd: '<%= yeoman.dist %>',
        src: '**/*',
        dest: grunt.option('dest') ? untildify(grunt.option('dest')):'',
        expand: true
      },
    },
    includes: {
      html:{

        src: ['<%= yeoman.app %>/index.html'], // Source files
        dest: '.tmp/index.html', // Destination directory
        options: {
          // flatten: true,
          silent: false,
          banner: ''
        }
      }
    },

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

    protractor: {
      options: {
        configFile: 'protractor.conf.js', // Default config file
        keepAlive: false, // If false, the grunt process stops when the test fails.
        noColor: false, // If true, protractor will not use colors in its output.
      },
      dev: {
        options: {
          args: {
            baseUrl: 'http://<%=hostname %>:<%=port %>',
            params: {
              appUrl: '/',
              apiUrl: 'http://fakeapi.stormpath.com:1337'
            }
          }
        }
      },
      sauce: {
        options: {
          configFile: 'protractor.conf.sauce.js',
          args: {
            sauceUser: process.env.SAUCE_USER,
            sauceKey: process.env.SAUCE_API_KEY,
            baseUrl: 'http://<%=hostname %>:<%=port %>',
            params: {
              appUrl: '/',
              apiUrl: 'http://fakeapi.stormpath.com:1337'
            }
          }
        }
      }
    },

    // 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',
    //         '<%= yeoman.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    concat: {
      dist: {
        options: {
          banner: '/*\n' +
            ' Stormpath ID Site v<%= pkg.version %>\n' +
            ' (c) 2014-2015 Stormpath, Inc. http://stormpath.com\n'+
            ' License: Apache 2.0\n' +
            '*/\n',
        },
        files: {
          'dist/scripts/app.js': ['dist/scripts/app.js']
        }
      }
    },

    // Test settings

    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      },
      liveunit: { // starts a karma server in the background
        configFile: 'karma.conf.js',
        background: true
      }
    },
  });

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

    grunt.task.run([
      'clean:server',
      'bowerInstall',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      // 'karma:liveunit',  // disabled until karama test runner is fixed
      'watch'
    ]);
  });

  grunt.registerTask('server', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

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

  grunt.registerTask('build', 'Build the project, assets are placed in dist/',
    [
      'clean:dist',
      'bowerInstall',
      'useminPrepare:dist',
      'concurrent:dist',
      'autoprefixer',
      'concat',
      'ngmin',
      'copy:dist',
      'cdnify',
      'cssmin',
      'uglify',
      // 'rev',
      'usemin',
      // 'htmlmin'
      'concat:dist'
    ]
  );

  grunt.registerTask('build:debug', [
    'clean:dist',
    'bowerInstall',
    'useminPrepare:debug',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'usemin',
    'copy:debug',
    'concat:dist'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);

  grunt.registerTask('dist',
    'Use this task to build a distro and copy it to the directoroy sepcified by --dist on the command line',
    function(){
      if(grunt.option('dest')){
        return grunt.task.run(['build','copy:dest']);
      }else{
        throw new Error('Must specify destination with --dest option');
      }
    }
  );
};
<commit_msg>Add banner info to vendor.js<commit_after>// Generated on 2014-05-01 using generator-angular 0.8.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'
//
var untildify = require('untildify');

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);

  // Define the configuration for all the tasks
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    hostname: process.env.SSO_DEV_HOST || 'localhost',
    port: process.env.SSO_DEV_PORT || 9000,

    // 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: ['bowerInstall']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all','karma:liveunit:run'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma:liveunit:run']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{less,css}'],
        tasks: ['less','autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      html: {
        files: ['<%= yeoman.app %>/{,*/}*.html'],
        tasks: ['includes:html']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: '<%=port %>',
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '0.0.0.0',
        livereload: 35729
      },
      livereload: {
        options: {
          open: 'http://<%=hostname %>:<%=port %>',
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= yeoman.dist %>'
        }
      }
    },

    less: {
      development:{
        files: {
          '.tmp/styles/main.css':'<%= yeoman.app %>/styles/main.less'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        '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 3 versions']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    bowerInstall: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath: '<%= yeoman.app %>/'
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          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: {
      dist:{
        src: '<%= yeoman.app %>/index.html',
        options: {
          dest: '<%= yeoman.dist %>',
          flow: {
            html: {
              steps: {
                js: ['concat', 'uglifyjs'],
                css: ['cssmin']
              },
              post: {}
            }
          }
        }
      },
      debug:{
        src: '<%= yeoman.app %>/index.html',
        options: {
          dest: '<%= yeoman.dist %>',
          flow: {
            html: {
              steps: {
                js: ['concat'],
                css: ['cssmin']
              },
              post: {}
            }
          }
        }
      }
    },

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

    // The following *-min tasks produce minified files in the dist folder
    cssmin: {
      options: {
        root: '<%= yeoman.app %>'
      }
    },

    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,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.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']
      }
    },

    // 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}',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        },{
          expand: true,
          cwd: '.tmp',
          dest: '<%= yeoman.dist %>',
          src: ['*.html']
        },{
          expand: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: ['error.html']
        }]
      },
      less: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.{less}'
      },
      debug: {
        expand: true,
        cwd: '.tmp/concat/scripts',
        dest: '<%= yeoman.dist %>/scripts',
        src: '{,*/}*.js'
      },
      dest:{
        cwd: '<%= yeoman.dist %>',
        src: '**/*',
        dest: grunt.option('dest') ? untildify(grunt.option('dest')):'',
        expand: true
      },
    },
    includes: {
      html:{

        src: ['<%= yeoman.app %>/index.html'], // Source files
        dest: '.tmp/index.html', // Destination directory
        options: {
          // flatten: true,
          silent: false,
          banner: ''
        }
      }
    },

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

    protractor: {
      options: {
        configFile: 'protractor.conf.js', // Default config file
        keepAlive: false, // If false, the grunt process stops when the test fails.
        noColor: false, // If true, protractor will not use colors in its output.
      },
      dev: {
        options: {
          args: {
            baseUrl: 'http://<%=hostname %>:<%=port %>',
            params: {
              appUrl: '/',
              apiUrl: 'http://fakeapi.stormpath.com:1337'
            }
          }
        }
      },
      sauce: {
        options: {
          configFile: 'protractor.conf.sauce.js',
          args: {
            sauceUser: process.env.SAUCE_USER,
            sauceKey: process.env.SAUCE_API_KEY,
            baseUrl: 'http://<%=hostname %>:<%=port %>',
            params: {
              appUrl: '/',
              apiUrl: 'http://fakeapi.stormpath.com:1337'
            }
          }
        }
      }
    },

    // 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',
    //         '<%= yeoman.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    concat: {
      dist: {
        options: {
          banner: '/*\n' +
            ' Stormpath ID Site v<%= pkg.version %>\n' +
            ' (c) 2014-2015 Stormpath, Inc. http://stormpath.com\n'+
            ' License: Apache 2.0\n' +
            '*/\n',
        },
        files: {
          'dist/scripts/app.js': ['dist/scripts/app.js'],
          'dist/scripts/vendor.js': ['dist/scripts/vendor.js']
        }
      }
    },

    // Test settings

    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      },
      liveunit: { // starts a karma server in the background
        configFile: 'karma.conf.js',
        background: true
      }
    },
  });

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

    grunt.task.run([
      'clean:server',
      'bowerInstall',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      // 'karma:liveunit',  // disabled until karama test runner is fixed
      'watch'
    ]);
  });

  grunt.registerTask('server', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

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

  grunt.registerTask('build', 'Build the project, assets are placed in dist/',
    [
      'clean:dist',
      'bowerInstall',
      'useminPrepare:dist',
      'concurrent:dist',
      'autoprefixer',
      'concat',
      'ngmin',
      'copy:dist',
      'cdnify',
      'cssmin',
      'uglify',
      // 'rev',
      'usemin',
      // 'htmlmin'
      'concat:dist'
    ]
  );

  grunt.registerTask('build:debug', [
    'clean:dist',
    'bowerInstall',
    'useminPrepare:debug',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'usemin',
    'copy:debug',
    'concat:dist'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);

  grunt.registerTask('dist',
    'Use this task to build a distro and copy it to the directoroy sepcified by --dist on the command line',
    function(){
      if(grunt.option('dest')){
        return grunt.task.run(['build','copy:dest']);
      }else{
        throw new Error('Must specify destination with --dest option');
      }
    }
  );
};
<|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}
                  />

                { showNotification
                  ? (
                    <Notification
                      title={`Vote for the January Charity Spotlight`}
                      message={`
                        Each month this year, we're highlighting a charity chosen by our
                        community. Nominate and vote for the nonprofit that means the most to you.`}
                      buttonText={'Vote'}
                      buttonURL={'https://goo.gl/forms/crNGLow4fg5fcKe63'}
                      style={{
                        marginTop: 4
                      }}
                    />
                  )
                  : 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
<commit_msg>Tweak style<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={`Vote for the January Charity Spotlight`}
                      message={`
                        Each month this year, we're highlighting a charity chosen by our
                        community. Nominate and vote for the nonprofit that means the most to you.`}
                      buttonText={'Vote'}
                      buttonURL={'https://goo.gl/forms/crNGLow4fg5fcKe63'}
                      style={{
                        marginTop: 4
                      }}
                    />
                  )
                  : 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|>


<commit_before>var chai = require('chai');
var expect = chai.expect;

var React = require('react');

/* eslint-disable no-console */
var consoleError = console.error;
function throwError(msg) {
  throw new Error(msg);
}
function hardFailConsole() {
  console.error = throwError;
}
function resetConsole() {
  console.error = consoleError;
}
/* eslint-enable no-console */

var sd = require('../skin-deep');

var $ = React.createElement;

describe("skin-deep", function() {

  beforeEach(hardFailConsole);
  afterEach(resetConsole);

  describe('rendering', function() {
    var Component = React.createClass({
      render: function() {
        return $('h1', { title: "blah" }, "Heading!");
      }
    });

    context('primitive ReactElement', function() {
      var tree = sd.shallowRender($('h1', { title: "blah" }, "Heading!"));
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });
    });

    context('component ReactElement', function() {
      var tree = sd.shallowRender($(Component));
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });
    });

    context('stateless component function ReactElement', function() {
      function PureComponent(props) {
        return $('h1', { title: 'blah' }, props.thing);
      }
      var tree = sd.shallowRender($(PureComponent, { thing: 'Heading!' }));
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });
    });

    context('with context', function() {

      var ContextComponent = React.createClass({
        contextTypes: { title: React.PropTypes.string },
        render: function() {
          return $('h1', { title: "blah" }, this.context.title);
        }
      });
      var tree = sd.shallowRender($(ContextComponent), { title: "Heading!" });
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });

    });
  });

  describe('.type', function() {
    var Component = React.createClass({
      render: function() {
        return $('h1', {}, "Hello");
      }
    });
    it('should access type for primitive ReactElement', function() {
      var tree = sd.shallowRender($('h1', {}));
      expect(tree.type).to.equal('h1');
    });
    it('should access type for another primitive ReactElement', function() {
      var tree = sd.shallowRender($('div', {}));
      expect(tree.type).to.equal('div');
    });
    it('should access type for Component ReactElement', function() {
      var tree = sd.shallowRender($(Component, {}));
      expect(tree.type).to.equal('h1');
    });
  });

  describe('.props', function() {
    var Component = React.createClass({
      render: function() {
        return $('h1', { blah: 1234, and: this.props.woo }, "Hello");
      }
    });
    context('on primitive ReactElement', function() {
      it('should access props', function() {
        var tree = sd.shallowRender($('h1', { a: 'b', c: 12 }));
        expect(tree.props).to.eql({ a: 'b', c: 12 });
      });
      it('should access other props', function() {
        var tree = sd.shallowRender($('h1', { a: 'b', d: 78 }));
        expect(tree.props).to.eql({ a: 'b', d: 78 });
      });
    });
    context('on rendered Component ReactElement', function() {
      it('should access props', function() {
        var tree = sd.shallowRender($(Component, { woo: 'hoo' }));
        expect(tree.props).to.eql({
          blah: 1234, and: 'hoo', children: "Hello"
        });
      });
      it('should access more props', function() {
        var tree = sd.shallowRender($(Component, { woo: 'girl' }));
        expect(tree.props).to.eql({
          blah: 1234, and: 'girl', children: "Hello"
        });
      });
    });
  });

  describe('reRender()', function() {
    var tree;
    var Component = React.createClass({
      render: function() {
        return $(this.props.tag || 'h1', {}, this.props.thing);
      }
    });
    beforeEach(function() {
      tree = sd.shallowRender($(Component, { thing: 'A' }));
    });
    it('should reRender with new props', function() {
      expect(tree.props).to.eql({ children: 'A' });
      tree.reRender({ thing: 'B' });
      expect(tree.props).to.eql({ children: 'B' });
    });
    it('should reRender with new and different props', function() {
      expect(tree.type).to.equal('h1');
      expect(tree.props).to.eql({ children: 'A' });
      tree.reRender({ tag: 'h6', thing: 'B' });
      expect(tree.type).to.equal('h6');
      expect(tree.props).to.eql({ children: 'B' });
    });

    context('multiple re-renders', function() {
      it('should overwrite multiple times', function() {
        expect(tree.props).to.eql({ children: 'A' });
        tree.reRender({ thing: 'X' });
        expect(tree.props).to.eql({ children: 'X' });
        tree.reRender({ thing: 123 });
        expect(tree.props).to.eql({ children: 123 });
        tree.reRender({ thing: false });
        expect(tree.props).to.eql({ children: false });
      });
    });
    context('with context', function() {
      var ContextComponent = React.createClass({
        contextTypes: { beep: React.PropTypes.bool },
        render: function() {
          return $('h1', {}, this.props.thing, this.context.beep);
        }
      });
      beforeEach(function() {
        tree = sd.shallowRender(
          $(ContextComponent, { thing: 'A' }),
          { beep: false }
        );
      });
      it('should keep previous context', function() {
        expect(tree.props).to.eql({ children: ['A', false] });
        tree.reRender({ thing: 'X' });
        expect(tree.props).to.eql({ children: ['X', false] });
      });
      it('should allow replacing context', function() {
        expect(tree.props).to.eql({ children: ['A', false] });
        tree.reRender({ thing: 'X' }, { beep: true });
        expect(tree.props).to.eql({ children: ['X', true] });
      });
    });
  });

  describe('text()', function() {
    var Widget = React.createClass({
      displayName: 'Widget',
      render: function() {
        return $('hr', {});
      }
    });
    it('should read primitive ReactElement', function() {
      var tree = sd.shallowRender($('h1', { not: 'this' }, 'Textually'));
      expect(tree.text()).to.eql('Textually');
    });
    it('should read ReactElement and children', function() {
      var tree = sd.shallowRender(
        $('ul', { not: 'this' },
          $('li', { nor: 'this' }, 'One'), ' ',
          $('li', { nor: 'this' }, 'Two'), ' ',
          $('li', { nor: 'this either' }, 'Three')
        )
      );
      expect(tree.text()).to.eql('One Two Three');
    });
    it('should collapse multiple spaces', function() {
      var tree = sd.shallowRender(
        $('div', {},
          $('p', {}, 'A  B  C'),
          '\n',
          '  ',
          $('p', {}, '  blah'))
      );
      expect(tree.text()).to.eql('A B C blah');
    });
    it('should not traverse inside children ReactElements', function() {
      var tree = sd.shallowRender(
        $('p', { attr: 'not this' },
          $('strong', {}, 'include this bit'),
          $(Widget, {}),
          $(Widget, {}, 'but not this'))
      );
      expect(tree.text()).to.eql('include this bit<Widget /><Widget />');
    });
  });

  describe("toString", function() {
    var Child = React.createClass({
      displayName: 'Child',
      render: function() {
        return $('p', {}, this.props.children);
      }
    });
    var Component = React.createClass({
      render: function() {
        return $('h1', { title: "blah" }, $(Child, { x: "y" }));
      }
    });
    it("should give HTML-like output", function() {
      var tree = sd.shallowRender($('h1', { title: "blah" }, "Heading!"));
      expect(String(tree)).to.eql('
\n  Heading!\n
');
    });
    it("should not expand child components", function() {
      var tree = sd.shallowRender($(Component));
      expect(String(tree))
        .to.eql('
\n  <Child x="y" />\n
');
    });
  });

  describe("hasClass", function() {
    it("should match whole class", function() {
      expect(sd.hasClass($('div', { className: "abc" }), "abc"))
        .to.eql(true);
    });
    it("should identify wrong whole class", function() {
      expect(sd.hasClass($('div', { className: "abc" }), "def"))
        .to.eql(false);
    });
    it("should match classes with dashes", function() {
      expect(sd.hasClass($('div', { className: "abc-123" }), "abc-123"))
        .to.eql(true);
    });
    it("should identify wrong class with dashes", function() {
      expect(sd.hasClass($('div', { className: "abc-23" }), "abc-234"))
        .to.eql(false);
    });
    it("should match one of multiple classes", function() {
      var node = $('div', { className: "space-after abc-123 space-before" });
      expect(sd.hasClass(node, "space-before")).to.eql(true);
      expect(sd.hasClass(node, "abc-123")).to.eql(true);
      expect(sd.hasClass(node, "space-after")).to.eql(true);
    });
    it("should match wrong in multiple classes", function() {
      var node = $('div', { className: "space-after abc-123 space-before" });
      expect(sd.hasClass(node, "space")).to.eql(false);
      expect(sd.hasClass(node, "abc")).to.eql(false);
      expect(sd.hasClass(node, "after")).to.eql(false);
      expect(sd.hasClass(node, "before")).to.eql(false);
    });
    it("should match classes with non-regex-safe characters", function() {
      expect(sd.hasClass($('div', { className: "???" }), "???"))
        .to.eql(true);
    });
  });

});

function isTree(obj) {
  expect(obj).to.contain.all.keys(['reRender', 'type', 'props', 'text']);
  expect(obj.props).to.be.an('object');
  return true;
}
<commit_msg>Rename made up attribute names to stop React 15.2 exploding in testsuite<commit_after>var chai = require('chai');
var expect = chai.expect;

var React = require('react');

/* eslint-disable no-console */
var consoleError = console.error;
function throwError(msg) {
  throw new Error(msg);
}
function hardFailConsole() {
  console.error = throwError;
}
function resetConsole() {
  console.error = consoleError;
}
/* eslint-enable no-console */

var sd = require('../skin-deep');

var $ = React.createElement;

describe("skin-deep", function() {

  beforeEach(hardFailConsole);
  afterEach(resetConsole);

  describe('rendering', function() {
    var Component = React.createClass({
      render: function() {
        return $('h1', { title: "blah" }, "Heading!");
      }
    });

    context('primitive ReactElement', function() {
      var tree = sd.shallowRender($('h1', { title: "blah" }, "Heading!"));
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });
    });

    context('component ReactElement', function() {
      var tree = sd.shallowRender($(Component));
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });
    });

    context('stateless component function ReactElement', function() {
      function PureComponent(props) {
        return $('h1', { title: 'blah' }, props.thing);
      }
      var tree = sd.shallowRender($(PureComponent, { thing: 'Heading!' }));
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });
    });

    context('with context', function() {

      var ContextComponent = React.createClass({
        contextTypes: { title: React.PropTypes.string },
        render: function() {
          return $('h1', { title: "blah" }, this.context.title);
        }
      });
      var tree = sd.shallowRender($(ContextComponent), { title: "Heading!" });
      var output = tree.getRenderOutput();

      it('should return a tree', function() {
        expect(tree).to.satisfy(isTree);
      });
      it('should have type in rendered output', function() {
        expect(output).to.have.property('type', 'h1');
      });
      it('should have props in rendered output', function() {
        expect(output).to.have.property('props');
        expect(output.props).to.eql({ title: "blah", children: "Heading!" });
      });

    });
  });

  describe('.type', function() {
    var Component = React.createClass({
      render: function() {
        return $('h1', {}, "Hello");
      }
    });
    it('should access type for primitive ReactElement', function() {
      var tree = sd.shallowRender($('h1', {}));
      expect(tree.type).to.equal('h1');
    });
    it('should access type for another primitive ReactElement', function() {
      var tree = sd.shallowRender($('div', {}));
      expect(tree.type).to.equal('div');
    });
    it('should access type for Component ReactElement', function() {
      var tree = sd.shallowRender($(Component, {}));
      expect(tree.type).to.equal('h1');
    });
  });

  describe('.props', function() {
    var Component = React.createClass({
      render: function() {
        return $('h1', { tabIndex: 1234, title: this.props.woo }, "Hello");
      }
    });
    context('on primitive ReactElement', function() {
      it('should access props', function() {
        var tree = sd.shallowRender($('h1', { a: 'b', c: 12 }));
        expect(tree.props).to.eql({ a: 'b', c: 12 });
      });
      it('should access other props', function() {
        var tree = sd.shallowRender($('h1', { a: 'b', d: 78 }));
        expect(tree.props).to.eql({ a: 'b', d: 78 });
      });
    });
    context('on rendered Component ReactElement', function() {
      it('should access props', function() {
        var tree = sd.shallowRender($(Component, { woo: 'hoo' }));
        expect(tree.props).to.eql({
          tabIndex: 1234, title: 'hoo', children: "Hello"
        });
      });
      it('should access more props', function() {
        var tree = sd.shallowRender($(Component, { woo: 'girl' }));
        expect(tree.props).to.eql({
          tabIndex: 1234, title: 'girl', children: "Hello"
        });
      });
    });
  });

  describe('reRender()', function() {
    var tree;
    var Component = React.createClass({
      render: function() {
        return $(this.props.tag || 'h1', {}, this.props.thing);
      }
    });
    beforeEach(function() {
      tree = sd.shallowRender($(Component, { thing: 'A' }));
    });
    it('should reRender with new props', function() {
      expect(tree.props).to.eql({ children: 'A' });
      tree.reRender({ thing: 'B' });
      expect(tree.props).to.eql({ children: 'B' });
    });
    it('should reRender with new and different props', function() {
      expect(tree.type).to.equal('h1');
      expect(tree.props).to.eql({ children: 'A' });
      tree.reRender({ tag: 'h6', thing: 'B' });
      expect(tree.type).to.equal('h6');
      expect(tree.props).to.eql({ children: 'B' });
    });

    context('multiple re-renders', function() {
      it('should overwrite multiple times', function() {
        expect(tree.props).to.eql({ children: 'A' });
        tree.reRender({ thing: 'X' });
        expect(tree.props).to.eql({ children: 'X' });
        tree.reRender({ thing: 123 });
        expect(tree.props).to.eql({ children: 123 });
        tree.reRender({ thing: false });
        expect(tree.props).to.eql({ children: false });
      });
    });
    context('with context', function() {
      var ContextComponent = React.createClass({
        contextTypes: { beep: React.PropTypes.bool },
        render: function() {
          return $('h1', {}, this.props.thing, this.context.beep);
        }
      });
      beforeEach(function() {
        tree = sd.shallowRender(
          $(ContextComponent, { thing: 'A' }),
          { beep: false }
        );
      });
      it('should keep previous context', function() {
        expect(tree.props).to.eql({ children: ['A', false] });
        tree.reRender({ thing: 'X' });
        expect(tree.props).to.eql({ children: ['X', false] });
      });
      it('should allow replacing context', function() {
        expect(tree.props).to.eql({ children: ['A', false] });
        tree.reRender({ thing: 'X' }, { beep: true });
        expect(tree.props).to.eql({ children: ['X', true] });
      });
    });
  });

  describe('text()', function() {
    var Widget = React.createClass({
      displayName: 'Widget',
      render: function() {
        return $('hr', {});
      }
    });
    it('should read primitive ReactElement', function() {
      var tree = sd.shallowRender($('h1', { not: 'this' }, 'Textually'));
      expect(tree.text()).to.eql('Textually');
    });
    it('should read ReactElement and children', function() {
      var tree = sd.shallowRender(
        $('ul', { not: 'this' },
          $('li', { nor: 'this' }, 'One'), ' ',
          $('li', { nor: 'this' }, 'Two'), ' ',
          $('li', { nor: 'this either' }, 'Three')
        )
      );
      expect(tree.text()).to.eql('One Two Three');
    });
    it('should collapse multiple spaces', function() {
      var tree = sd.shallowRender(
        $('div', {},
          $('p', {}, 'A  B  C'),
          '\n',
          '  ',
          $('p', {}, '  blah'))
      );
      expect(tree.text()).to.eql('A B C blah');
    });
    it('should not traverse inside children ReactElements', function() {
      var tree = sd.shallowRender(
        $('p', { attr: 'not this' },
          $('strong', {}, 'include this bit'),
          $(Widget, {}),
          $(Widget, {}, 'but not this'))
      );
      expect(tree.text()).to.eql('include this bit<Widget /><Widget />');
    });
  });

  describe("toString", function() {
    var Child = React.createClass({
      displayName: 'Child',
      render: function() {
        return $('p', {}, this.props.children);
      }
    });
    var Component = React.createClass({
      render: function() {
        return $('h1', { title: "blah" }, $(Child, { x: "y" }));
      }
    });
    it("should give HTML-like output", function() {
      var tree = sd.shallowRender($('h1', { title: "blah" }, "Heading!"));
      expect(String(tree)).to.eql('
\n  Heading!\n
');
    });
    it("should not expand child components", function() {
      var tree = sd.shallowRender($(Component));
      expect(String(tree))
        .to.eql('
\n  <Child x="y" />\n
');
    });
  });

  describe("hasClass", function() {
    it("should match whole class", function() {
      expect(sd.hasClass($('div', { className: "abc" }), "abc"))
        .to.eql(true);
    });
    it("should identify wrong whole class", function() {
      expect(sd.hasClass($('div', { className: "abc" }), "def"))
        .to.eql(false);
    });
    it("should match classes with dashes", function() {
      expect(sd.hasClass($('div', { className: "abc-123" }), "abc-123"))
        .to.eql(true);
    });
    it("should identify wrong class with dashes", function() {
      expect(sd.hasClass($('div', { className: "abc-23" }), "abc-234"))
        .to.eql(false);
    });
    it("should match one of multiple classes", function() {
      var node = $('div', { className: "space-after abc-123 space-before" });
      expect(sd.hasClass(node, "space-before")).to.eql(true);
      expect(sd.hasClass(node, "abc-123")).to.eql(true);
      expect(sd.hasClass(node, "space-after")).to.eql(true);
    });
    it("should match wrong in multiple classes", function() {
      var node = $('div', { className: "space-after abc-123 space-before" });
      expect(sd.hasClass(node, "space")).to.eql(false);
      expect(sd.hasClass(node, "abc")).to.eql(false);
      expect(sd.hasClass(node, "after")).to.eql(false);
      expect(sd.hasClass(node, "before")).to.eql(false);
    });
    it("should match classes with non-regex-safe characters", function() {
      expect(sd.hasClass($('div', { className: "???" }), "???"))
        .to.eql(true);
    });
  });

});

function isTree(obj) {
  expect(obj).to.contain.all.keys(['reRender', 'type', 'props', 'text']);
  expect(obj.props).to.be.an('object');
  return true;
}
<|endoftext|>


<commit_before>var semver = require('semver'),
    f = require('util').format,
    files = {
      gw2Api: [
      'src/gw2Api.js',
      'src/Error.js',
      'src/ajax.js',
      'src/utils.js',
      'src/config.js',
      'src/ResponseHeader.js',
      'src/Parameters.js',
      'src/Endpoint.js',
      'src/Endpoints.js'
      ]
    };

module.exports = function(grunt) {
  grunt.initConfig({
    version: grunt.file.readJSON('package.json').version,

    tempDir: 'dist_temp',
    buildDir: 'dist',

    banner: [
      '/*!',
      ' * gw2Api.js <%= version %>',
      ' * https://github.com/dyanarose/gw2Api.js',
      ' * Copyright 2015-<%= grunt.template.today("yyyy") %> Dyana Rose; Licensed MIT',
      ' */\n\n'
    ].join('\n'),
    karma: {
      dist: {
        configFile: 'karma.conf.js',
        singleRun: true
      },
      offline:{
        configFile: 'karma.offline.conf.js',
        singleRun: true
      },
      unit:{
        configFile: 'karma.unit.conf.js',
        singleRun: true
      }
    },
    uglify: {
      options: {
        banner: '<%= banner %>'
      },

      concatgw2Api: {
        options: {
          mangle: false,
          beautify: true,
          compress: false,
          banner: ''
        },
        src: files.gw2Api,
        dest: '<%= tempDir %>/gw2Api.js'
      },
      gw2Api: {
        options: {
          mangle: false,
          beautify: true,
          compress: false
        },
        src: '<%= tempDir %>/gw2Api.js',
        dest: '<%= buildDir %>/gw2Api.js'
      },
      gw2ApiMin: {
        options: {
          mangle: true,
          compress: {}
        },
        src: '<%= tempDir %>/gw2Api.js',
        dest: '<%= buildDir %>/gw2Api.min.js'
      }
    },

    umd: {
      gw2Api: {
        src: '<%= tempDir %>/gw2Api.js',
        objectToExport: 'gw2Api',
        amdModuleId: 'gw2Api',
        deps: {
          default: ['Q'],
          amd: ['Q'],
          cjs: ['Q'],
          global: ['Q']
        }
      }
    },

    sed: {
      version: {
        pattern: '%VERSION%',
        replacement: '<%= version %>',
        recursive: true,
        path: '<%= buildDir %>'
      }
    },

    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      src: 'src/**/*.js',
      test: ['test/**/*_spec.js', 'test/integration/test.js'],
      gruntfile: ['Gruntfile.js']
    },

    watch: {
      js: {
        files: 'src/**/*',
        tasks: 'build'
      }
    },

    clean: {
      dist: 'dist'
    },

    connect: {
      server: {
        options: { port: 8888, keepalive: true }
      }
    },

    concurrent: {
      options: { logConcurrentOutput: true },
      dev: ['server', 'watch']
    },

    step: {
      options: {
        option: false
      }
    }
  });

  grunt.registerTask('release', '#shipit', function(version) {
    var curVersion = grunt.config.get('version');

    version = semver.inc(curVersion, version) || version;

    if (!semver.valid(version) || semver.lte(version, curVersion)) {
      grunt.fatal(':( version fail');
    }

    grunt.config.set('version', version);

    grunt.task.run([
      'exec:git_on_master',
      'exec:git_is_clean',
      f('step:Update to version %s?', version),
      f('manifests:%s', version),
      'build',
      'exec:git_add',
      f('exec:git_commit:%s', version),
      f('exec:git_tag:%s', version),
      //'step:Push changes?',
      //'exec:git_push',
      //'step:Publish to npm?',
      //'exec:npm_publish',
      //'step:Publish assets?',
      //'exec:publish_assets'
    ]);
  });

  grunt.registerTask('manifests', 'Update manifests.', function(version) {
    var _ = grunt.util._,
        pkg = grunt.file.readJSON('package.json'),
        bower = grunt.file.readJSON('bower.json');

    bower = JSON.stringify(_.extend(bower, {
      name: pkg.name,
      version: version
    }), null, 2);

    pkg = JSON.stringify(_.extend(pkg, {
      version: version
    }), null, 2);

    grunt.file.write('package.json', pkg);
    grunt.file.write('bower.json', bower);
  });

  // aliases
  // -------

  grunt.registerTask('default', 'build');
  grunt.registerTask('server', 'connect:server');
  grunt.registerTask('lint', 'jshint');
  grunt.registerTask('dev', ['build', 'concurrent:dev']);
  grunt.registerTask('build', [
    'uglify:concatgw2Api',
    'umd:gw2Api',
    'uglify:gw2Api',
    'uglify:gw2ApiMin',
    'sed:version'
  ]);

  // load tasks
  // ----------

  grunt.loadNpmTasks('grunt-umd');
  grunt.loadNpmTasks('grunt-sed');
  grunt.loadNpmTasks('grunt-exec');
  grunt.loadNpmTasks('grunt-step');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-concurrent');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
};
<commit_msg>cleaning task<commit_after>var semver = require('semver'),
    f = require('util').format,
    files = {
      gw2Api: [
      'src/gw2Api.js',
      'src/Error.js',
      'src/ajax.js',
      'src/utils.js',
      'src/config.js',
      'src/ResponseHeader.js',
      'src/Parameters.js',
      'src/Endpoint.js',
      'src/Endpoints.js'
      ]
    };

module.exports = function(grunt) {
  grunt.initConfig({
    version: grunt.file.readJSON('package.json').version,

    tempDir: 'dist_temp',
    buildDir: 'dist',

    banner: [
      '/*!',
      ' * gw2Api.js <%= version %>',
      ' * https://github.com/dyanarose/gw2Api.js',
      ' * Copyright 2015-<%= grunt.template.today("yyyy") %> Dyana Rose; Licensed MIT',
      ' */\n\n'
    ].join('\n'),
    karma: {
      dist: {
        configFile: 'karma.conf.js',
        singleRun: true
      },
      offline:{
        configFile: 'karma.offline.conf.js',
        singleRun: true
      },
      unit:{
        configFile: 'karma.unit.conf.js',
        singleRun: true
      }
    },
    uglify: {
      options: {
        banner: '<%= banner %>'
      },

      concatgw2Api: {
        options: {
          mangle: false,
          beautify: true,
          compress: false,
          banner: ''
        },
        src: files.gw2Api,
        dest: '<%= tempDir %>/gw2Api.js'
      },
      gw2Api: {
        options: {
          mangle: false,
          beautify: true,
          compress: false
        },
        src: '<%= tempDir %>/gw2Api.js',
        dest: '<%= buildDir %>/gw2Api.js'
      },
      gw2ApiMin: {
        options: {
          mangle: true,
          compress: {}
        },
        src: '<%= tempDir %>/gw2Api.js',
        dest: '<%= buildDir %>/gw2Api.min.js'
      }
    },

    umd: {
      gw2Api: {
        src: '<%= tempDir %>/gw2Api.js',
        objectToExport: 'gw2Api',
        amdModuleId: 'gw2Api',
        deps: {
          default: ['Q'],
          amd: ['Q'],
          cjs: ['Q'],
          global: ['Q']
        }
      }
    },

    sed: {
      version: {
        pattern: '%VERSION%',
        replacement: '<%= version %>',
        recursive: true,
        path: '<%= buildDir %>'
      }
    },

    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      src: 'src/**/*.js',
      test: ['test/**/*_spec.js', 'test/integration/test.js'],
      gruntfile: ['Gruntfile.js']
    },

    watch: {
      js: {
        files: 'src/**/*',
        tasks: 'build'
      }
    },

    clean: {
      dist: 'dist'
    },

    connect: {
      server: {
        options: { port: 8888, keepalive: true }
      }
    },

    concurrent: {
      options: { logConcurrentOutput: true },
      dev: ['server', 'watch']
    },

    step: {
      options: {
        option: false
      }
    }
  });

  grunt.registerTask('release', '#shipit', function(version) {
    var curVersion = grunt.config.get('version');

    version = semver.inc(curVersion, version) || version;

    if (!semver.valid(version) || semver.lte(version, curVersion)) {
      grunt.fatal(':( version fail');
    }

    grunt.config.set('version', version);

    grunt.task.run([
      'exec:git_on_master',
      'exec:git_is_clean',
      f('step:Update to version %s?', version),
      f('manifests:%s', version),
      'build',
      'exec:git_add',
      f('exec:git_commit:%s', version),
      f('exec:git_tag:%s', version)
    ]);
  });

  grunt.registerTask('manifests', 'Update manifests.', function(version) {
    var _ = grunt.util._,
        pkg = grunt.file.readJSON('package.json'),
        bower = grunt.file.readJSON('bower.json');

    bower = JSON.stringify(_.extend(bower, {
      name: pkg.name,
      version: version
    }), null, 2);

    pkg = JSON.stringify(_.extend(pkg, {
      version: version
    }), null, 2);

    grunt.file.write('package.json', pkg);
    grunt.file.write('bower.json', bower);
  });

  // aliases
  // -------

  grunt.registerTask('default', 'build');
  grunt.registerTask('server', 'connect:server');
  grunt.registerTask('lint', 'jshint');
  grunt.registerTask('dev', ['build', 'concurrent:dev']);
  grunt.registerTask('build', [
    'uglify:concatgw2Api',
    'umd:gw2Api',
    'uglify:gw2Api',
    'uglify:gw2ApiMin',
    'sed:version'
  ]);

  // load tasks
  // ----------

  grunt.loadNpmTasks('grunt-umd');
  grunt.loadNpmTasks('grunt-sed');
  grunt.loadNpmTasks('grunt-exec');
  grunt.loadNpmTasks('grunt-step');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-concurrent');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
};
<|endoftext|>


<commit_before>(function () {
  'use strict';

  /*ICON RESPONSIBLE TO ADD A NEW ELEMENT TO THE INTRODUCED MODEL*/

  angular
    .module('webApp')
    .directive('cAddElement', cAddElement);

  function cAddElement() {
    var directive = {
      restrict: 'E',
      scope: {
        iconClass: "=iconClass",
        model: "=model",
        type: "=type",
        disabled: "=disabled",
        qa: "@",
        pattern: "=",
        placeholder: "@",
        error: '=',
        submittedForm: '=',
        label: '=',
        help: '=',
        limit: '='
      },
      replace: "true",
      templateUrl: 'templates/components/c-add-element.tpl.html',
      link: link
    };

    return directive;

    function link(scope) {
      scope.addInput = addInput;
      scope.errorLimit = false;

      function addInput(event) {
        if (scope.inputToAdd !== '' && scope.inputToAdd !== undefined && (event.keyCode == '13' || event.type === "click")) {
          var inputExists = false;
          scope.errorLimit = false;
          if (scope.limit === 0 || scope.model.length < scope.limit) {
            for (var i = 0; i < scope.model.length; i++) {
              if (scope.model[i].name === scope.inputToAdd) {
                inputExists = true;
              }
            }
            if (inputExists) {
              scope.submittedForm = true;
              scope.error = true;
            }
            else {
              scope.submittedForm = false;
              scope.error = false;
              scope.model.push({name:scope.inputToAdd});
              scope.inputToAdd = '';
            }
            event.preventDefault();
          }
          else {
            scope.submittedForm = true;
            scope.errorLimit = true;
          }
        }
      }
    }
  }
})();
<commit_msg>refactor(c-add-element): Refactor c-add-element to allow add objects or strings<commit_after>(function () {
  'use strict';

  /*ICON RESPONSIBLE TO ADD A NEW ELEMENT TO THE INTRODUCED MODEL*/

  angular
    .module('webApp')
    .directive('cAddElement', cAddElement);

  function cAddElement() {
    var directive = {
      restrict: 'E',
      scope: {
        iconClass: "=iconClass",
        model: "=model",
        type: "=type",
        disabled: "=disabled",
        qa: "@",
        pattern: "=",
        placeholder: "@",
        error: '=',
        submittedForm: '=',
        label: '=',
        help: '=',
        limit: '=',
        isObject: "=",
        attribute: "@",
        required:"="
      },
      replace: "true",
      templateUrl: 'templates/components/c-add-element.tpl.html',
      link: link
    };

    return directive;

    function link(scope) {
      scope.addInput = addInput;
      scope.errorLimit = false;
      function addInput(event) {
        if (scope.inputToAdd !== '' && scope.inputToAdd !== undefined && (event.keyCode == '13' || event.type === "click")) {
          var inputExists = false;
          scope.errorLimit = false;
          if (scope.limit === 0 || scope.model.length < scope.limit) {
            for (var i = 0; i < scope.model.length; i++) {
              if (scope.model[i].name === scope.inputToAdd) {
                inputExists = true;
              }
            }
            if (inputExists) {
              scope.submittedForm = true;
              scope.error = true;
            }
            else {
              scope.submittedForm = false;
              scope.error = false;

              if (scope.isObject) {
                var objectToAdd = {};
                objectToAdd[scope.attribute] =  scope.inputToAdd;
                scope.model.push(objectToAdd);
              } else
                scope.model.push(scope.inputToAdd);
              scope.inputToAdd = '';
            }
            event.preventDefault();
          }
          else {
            scope.submittedForm = true;
            scope.errorLimit = true;
          }
        }
      }
    }
  }
})();
<|endoftext|>


<commit_before>var chai    = require('chai'),
    expect  = chai.expect,
    should  = chai.should();
var OSI     = require('../index.js');

describe('OSI Basic usage', function(){

    it('OSI is available', function () {
        expect(OSI).to.be.a('object');
    });

    it('OSI.requestService', function () {
        var testVar;
        var expectedVar = 10;
        var srvName = "TestService";
        var fnResolver = function () {
            testVar = expectedVar;
        };

        OSI.requestService(srvName, fnResolver);

        should.not.exist(testVar);

        OSI.registerService(srvName);

    });

});<commit_msg>Extend OSI tests<commit_after>var chai    = require('chai'),
    expect  = chai.expect,
    should  = chai.should();
var OSI     = require('../index.js');

describe('OSI Basic usage', function(){

    it('OSI is available', function () {
        expect(OSI).to.be.a('object');
    });

    it('OSI.registerService', function () {
        expect(OSI.registerService).to.be.a('function');
    });

    it('OSI.requestService', function () {
        var testVar;
        var expectedVar = 10;
        var srvName = "TestService";
        var fnResolver = function () {
            testVar = expectedVar;
        };

        expect(OSI.requestService).to.be.a('function');

        OSI.requestService(srvName, fnResolver);

        should.not.exist(testVar);

        OSI.registerService(srvName);

        expect(testVar).to.be.a('number', expectedVar);
    });
});<|endoftext|>