<commit_before>// Generated on 2014-05-05 using generator-angular-fullstack 1.4.2
'use strict';

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({

        // Project settings
        yeoman: {
            // configurable paths
            app: require('./bower.json').appPath || 'app',
            dist: 'dist'
        },
        express: {
            options: {
                port: process.env.PORT || 9000
            },
            dev: {
                options: {
                    script: 'server.js',
                    debug: true,
                    node_env: 'development'
                }
            },
            prod: {
                options: {
                    script: 'dist/server.js',
                    node_env: 'production'
                }
            }
        },
        open: {
            server: {
                url: 'http://localhost:<%= express.options.port %>'
            }
        },
        watch: {
            js: {
                files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
                tasks: ['newer:jshint:all'],
                options: {
                    livereload: true
                }
            },
            sass: {
                // TODO: Add the task needed for libsass to compile the .sass files
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['sass', 'autoprefixer']
            },
            gruntfile: {
                files: ['Gruntfile.js']
            },
            livereload: {
                files: [
                    '<%= yeoman.app %>/views/{,*//*}*.{html,jade}',
                    '{.tmp,<%= yeoman.app %>}/styles/{,*//*}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*//*}*.js',
                    '<%= yeoman.app %>/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}'
                ],

                options: {
                    livereload: true
                }
            },
            express: {
                files: [
                    'server.js',
                    'lib/**/*.{js,json}'
                ],
                tasks: ['newer:jshint:server', 'express:dev', 'wait'],
                options: {
                    livereload: true,
                    nospawn: true //Without this option specified express won't be reloaded
                }
            }
        },

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

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

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

        // Use nodemon to run server in debug mode with an initial breakpoint
        nodemon: {
            debug: {
                script: 'server.js',
                options: {
                    nodeArgs: ['--debug-brk'],
                    env: {
                        PORT: process.env.PORT || 9000
                    },
                    callback: function (nodemon) {
                        nodemon.on('log', function (event) {
                            console.log(event.colour);
                        });

                        // opens browser on initial server start
                        nodemon.on('config:update', function () {
                            setTimeout(function () {
                                require('open')('http://localhost:8080/debug?port=5858');
                            }, 500);
                        });
                    }
                }
            }
        },

        // Automatically inject Bower components into the app
        // The grunt-bower-install was renamed to wiredep.
        wiredep: {
            app: {
                src: '<%= yeoman.app %>/views/index.jade',
                ignorePath: '..',
                exclude: []
            },
            sass: {
                src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}']
            },
        },

        // 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 %>/public/images/generated'
                }
            },
            server: {
                options: {
                    debugInfo: true
                }
            }
        },
        */
        // This replaces compass as it has Ruby dependencies.
        sass: {
            dist: {
                files: {
                    '.tmp/styles/main.css': '<%= yeoman.app %>/styles/main.scss'
                }
            }
        },

        // Renames files for browser caching purposes
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/public/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/public/styles/{,*/}*.css',
                        // '<%= yeoman.dist %>/public/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                        // '<%= yeoman.dist %>/public/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 %>/views/index.html',
                '<%= yeoman.app %>/views/index.jade'],
            options: {
                dest: '<%= yeoman.dist %>/public',
                root: '.tmp'
            }
        },

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

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            options : {
                cache: false
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg,gif}',
                    dest: '<%= yeoman.dist %>/public/images'
                }]
            }
        },

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

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

        // Allow the use of non-minsafe AngularJS files. Automatically makes it
        // minsafe compatible so Uglify does not destroy the ng references
        // ngmin: {
        //     dist: {
        //         files: [{
        //             expand: true,
        //             cwd: '.tmp/concat/scripts',
        //             src: '*.js',
        //             dest: '.tmp/concat/scripts'
        //         }]
        //     }
        // },

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

        // Copies remaining files to places other tasks can use
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>/public',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.{webp,png,jpeg,jpg,svg,gif,cur}'
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '.tmp',
                    src: [
                        'bower_components/**/*',
                        'scripts/**/*',
                        'styles/*.css'
                    ]
                }, {
                    expand: true,
                    flatten: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/bower_components/',
                    dest: '<%= yeoman.dist %>/public/styles/fonts/',
                    src: [
                        '**/fonts/{bootstrap/,}*.eot',
                        '**/fonts/{bootstrap/,}*.svg',
                        '**/fonts/{bootstrap/,}*.ttf',
                        '**/fonts/{bootstrap/,}*.woff',
                    ]
                }, {
                    expand: true,
                    flatten: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/styles/fonts/',
                    dest: '<%= yeoman.dist %>/public/styles/fonts/',
                    src: [
                        '*'
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/views',
                    dest: '<%= yeoman.dist %>/views',
                    src: '**/*.jade'
                },{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/includes',
                    dest: '<%= yeoman.dist %>/includes',
                    src: '**/*.jade'
                },
                {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/public/images',
                    src: ['generated/*']
                }, {
                    expand: true,
                    dest: '<%= yeoman.dist %>',
                    src: [
                        'package.json',
                        'server.js',
                        'lib/**/*',
                        'indexing/**'
                    ]
                }]
            },
            temp: {
                expand: true,
                flatten: true,
                dot: true,
                cwd: '<%= yeoman.app %>/bower_components/',
                dest: '.tmp/styles/fonts/',
                src: [
                    '**/fonts/{bootstrap/,}*.eot',
                    '**/fonts/{bootstrap/,}*.svg',
                    '**/fonts/{bootstrap/,}*.ttf',
                    '**/fonts/{bootstrap/,}*.woff',
                ]
            }
        },

        // Run some tasks in parallel to speed up the build process
        concurrent: {
            server: [
                'sass'
            ],
            debug: {
                tasks: [
                    'nodemon'
                ],
                options: {
                    logConcurrentOutput: true
                }
            },
            dist: [
                'sass',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },

        // 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 %>/public/styles/main.css': [
        //                 '.tmp/styles/{,*/}*.css',
        //                 '<%= yeoman.app %>/styles/{,*/}*.css'
        //             ]
        //         }
        //     }
        // },
        // uglify: {
        //     dist: {
        //         files: {
        //             '<%= yeoman.dist %>/public/scripts/main.js': [
        //                 '<%= yeoman.dist %>/public/scripts/main.js'
        //             ]
        //         }
        //     }
        // },
        // concat: {
        //     dist: { }
        // }
    });

    // Used for delaying livereload until after server has restarted
    grunt.registerTask('wait', function () {
        grunt.log.ok('Waiting for server reload...');

        var done = this.async();

        setTimeout(function () {
            grunt.log.writeln('Done waiting!');
            done();
        }, 500);
    });

    grunt.registerTask('express-keepalive', 'Keep grunt running', function() {
        this.async();
    });

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

        if (target === 'debug') {
            return grunt.task.run([
                'clean:server',
                'wiredep',
                'concurrent:server',
                'autoprefixer',
                'concurrent:debug'
            ]);
        }

        grunt.task.run([
            'clean:server',
            'copy:temp',
            'sass',
            'wiredep',
            'concurrent:server',
            'autoprefixer',
            'express:dev',
            //'open',
            'watch'
        ]);
    });

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

    grunt.registerTask('build', [
        'clean:dist',
        'sass',
        'wiredep',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'copy:dist',
        'concat',
        // 'ngmin',
        // 'cdnify',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        'newer:jshint',
        'build'
    ]);
};
<commit_msg>no jshint in grunt<commit_after>// Generated on 2014-05-05 using generator-angular-fullstack 1.4.2
'use strict';

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({

        // Project settings
        yeoman: {
            // configurable paths
            app: require('./bower.json').appPath || 'app',
            dist: 'dist'
        },
        express: {
            options: {
                port: process.env.PORT || 9000
            },
            dev: {
                options: {
                    script: 'server.js',
                    debug: true,
                    node_env: 'development'
                }
            },
            prod: {
                options: {
                    script: 'dist/server.js',
                    node_env: 'production'
                }
            }
        },
        open: {
            server: {
                url: 'http://localhost:<%= express.options.port %>'
            }
        },
        watch: {
            js: {
                files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
                options: {
                    livereload: true
                }
            },
            sass: {
                // TODO: Add the task needed for libsass to compile the .sass files
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['sass', 'autoprefixer']
            },
            gruntfile: {
                files: ['Gruntfile.js']
            },
            livereload: {
                files: [
                    '<%= yeoman.app %>/views/{,*//*}*.{html,jade}',
                    '{.tmp,<%= yeoman.app %>}/styles/{,*//*}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*//*}*.js',
                    '<%= yeoman.app %>/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}'
                ],

                options: {
                    livereload: true
                }
            },
            express: {
                files: [
                    'server.js',
                    'lib/**/*.{js,json}'
                ],
          tasks: ['express:dev', 'wait'],
                options: {
                    livereload: true,
                    nospawn: true //Without this option specified express won't be reloaded
                }
            }
        },

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

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

        // Use nodemon to run server in debug mode with an initial breakpoint
        nodemon: {
            debug: {
                script: 'server.js',
                options: {
                    nodeArgs: ['--debug-brk'],
                    env: {
                        PORT: process.env.PORT || 9000
                    },
                    callback: function (nodemon) {
                        nodemon.on('log', function (event) {
                            console.log(event.colour);
                        });

                        // opens browser on initial server start
                        nodemon.on('config:update', function () {
                            setTimeout(function () {
                                require('open')('http://localhost:8080/debug?port=5858');
                            }, 500);
                        });
                    }
                }
            }
        },

        // Automatically inject Bower components into the app
        // The grunt-bower-install was renamed to wiredep.
        wiredep: {
            app: {
                src: '<%= yeoman.app %>/views/index.jade',
                ignorePath: '..',
                exclude: []
            },
            sass: {
                src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}']
            },
        },

        // 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 %>/public/images/generated'
                }
            },
            server: {
                options: {
                    debugInfo: true
                }
            }
        },
        */
        // This replaces compass as it has Ruby dependencies.
        sass: {
            dist: {
                files: {
                    '.tmp/styles/main.css': '<%= yeoman.app %>/styles/main.scss'
                }
            }
        },

        // Renames files for browser caching purposes
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/public/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/public/styles/{,*/}*.css',
                        // '<%= yeoman.dist %>/public/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                        // '<%= yeoman.dist %>/public/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 %>/views/index.html',
                '<%= yeoman.app %>/views/index.jade'],
            options: {
                dest: '<%= yeoman.dist %>/public',
                root: '.tmp'
            }
        },

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

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            options : {
                cache: false
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg,gif}',
                    dest: '<%= yeoman.dist %>/public/images'
                }]
            }
        },

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

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

        // Allow the use of non-minsafe AngularJS files. Automatically makes it
        // minsafe compatible so Uglify does not destroy the ng references
        // ngmin: {
        //     dist: {
        //         files: [{
        //             expand: true,
        //             cwd: '.tmp/concat/scripts',
        //             src: '*.js',
        //             dest: '.tmp/concat/scripts'
        //         }]
        //     }
        // },

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

        // Copies remaining files to places other tasks can use
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>/public',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.{webp,png,jpeg,jpg,svg,gif,cur}'
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '.tmp',
                    src: [
                        'bower_components/**/*',
                        'scripts/**/*',
                        'styles/*.css'
                    ]
                }, {
                    expand: true,
                    flatten: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/bower_components/',
                    dest: '<%= yeoman.dist %>/public/styles/fonts/',
                    src: [
                        '**/fonts/{bootstrap/,}*.eot',
                        '**/fonts/{bootstrap/,}*.svg',
                        '**/fonts/{bootstrap/,}*.ttf',
                        '**/fonts/{bootstrap/,}*.woff',
                    ]
                }, {
                    expand: true,
                    flatten: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/styles/fonts/',
                    dest: '<%= yeoman.dist %>/public/styles/fonts/',
                    src: [
                        '*'
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/views',
                    dest: '<%= yeoman.dist %>/views',
                    src: '**/*.jade'
                },{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/includes',
                    dest: '<%= yeoman.dist %>/includes',
                    src: '**/*.jade'
                },
                {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/public/images',
                    src: ['generated/*']
                }, {
                    expand: true,
                    dest: '<%= yeoman.dist %>',
                    src: [
                        'package.json',
                        'server.js',
                        'lib/**/*',
                        'indexing/**'
                    ]
                }]
            },
            temp: {
                expand: true,
                flatten: true,
                dot: true,
                cwd: '<%= yeoman.app %>/bower_components/',
                dest: '.tmp/styles/fonts/',
                src: [
                    '**/fonts/{bootstrap/,}*.eot',
                    '**/fonts/{bootstrap/,}*.svg',
                    '**/fonts/{bootstrap/,}*.ttf',
                    '**/fonts/{bootstrap/,}*.woff',
                ]
            }
        },

        // Run some tasks in parallel to speed up the build process
        concurrent: {
            server: [
                'sass'
            ],
            debug: {
                tasks: [
                    'nodemon'
                ],
                options: {
                    logConcurrentOutput: true
                }
            },
            dist: [
                'sass',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },

        // 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 %>/public/styles/main.css': [
        //                 '.tmp/styles/{,*/}*.css',
        //                 '<%= yeoman.app %>/styles/{,*/}*.css'
        //             ]
        //         }
        //     }
        // },
        // uglify: {
        //     dist: {
        //         files: {
        //             '<%= yeoman.dist %>/public/scripts/main.js': [
        //                 '<%= yeoman.dist %>/public/scripts/main.js'
        //             ]
        //         }
        //     }
        // },
        // concat: {
        //     dist: { }
        // }
    });

    // Used for delaying livereload until after server has restarted
    grunt.registerTask('wait', function () {
        grunt.log.ok('Waiting for server reload...');

        var done = this.async();

        setTimeout(function () {
            grunt.log.writeln('Done waiting!');
            done();
        }, 500);
    });

    grunt.registerTask('express-keepalive', 'Keep grunt running', function() {
        this.async();
    });

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

        if (target === 'debug') {
            return grunt.task.run([
                'clean:server',
                'wiredep',
                'concurrent:server',
                'autoprefixer',
                'concurrent:debug'
            ]);
        }

        grunt.task.run([
            'clean:server',
            'copy:temp',
            'sass',
            'wiredep',
            'concurrent:server',
            'autoprefixer',
            'express:dev',
            //'open',
            'watch'
        ]);
    });

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

    grunt.registerTask('build', [
        'clean:dist',
        'sass',
        'wiredep',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'copy:dist',
        'concat',
        // 'ngmin',
        // 'cdnify',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
    ]);

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


<commit_before>/*jshint camelcase: false */

'use strict';

module.exports = function (grunt) {

  var app = {
    files: {
      js: [
        'Gruntfile.js',
        'generators/{,**/}*.js',
        'test/{,**/}*.js'
      ]
    }
  };

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

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      gruntfile: {
        files: ['Gruntfile.js']
      },
      js: {
        files: app.files.js,
        tasks: ['jshint', 'jscs', 'mochacov']
      }
    },

    // JavaScript Lint
    jshint: {
      options: {
        jshintrc: true,
        reporter: require('jshint-stylish')
      },
      all: app.files.js
    },

    // JavaScript Code Style check
    jscs: {
      all: app.files.js
    },

    shell: {
      options: {
        stdout: true,
        stderr: false
      },
      test: {
        command: 'npm test'
      }
    },

    mochaTest: {
      test: {
        options: {
          reporter: 'spec'
        },
        src: 'test/index.js'
      }
    },

    mocha_istanbul: {
      coverage: {
        src: 'test/index.js',
        options: {
          quiet: true
        }
      },
      coveralls: {
        src: 'test/index.js',
        options: {
          quiet: true,
          coverage:true,
          check: {
            lines: 75,
            statements: 75
          },
          root: 'app',
          reportFormats: ['cobertura','lcovonly']
        }
      }
    },

    clean: {
      temp: ['.tmp'],
      coverage: ['test/coverage.html']
    }
  });

  grunt.event.on('coverage', function (lcov, done) {
    require('coveralls').handleInput(lcov, function (err) {
      if (err) {
        return done(err);
      }
      done();
    });
  });

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

  grunt.registerTask('mocha', ['mochaTest:test']);
  grunt.registerTask('coverage', ['mocha_istanbul:coverage']);
  grunt.registerTask('coveralls', ['mocha_istanbul:coveralls']);

  grunt.registerTask('test', ['clean', 'jshint', 'jscs', 'coverage', 'mocha']);
  grunt.registerTask('travis', ['jshint', 'jscs', 'coverage', 'coveralls', 'mocha']);
  grunt.registerTask('default', ['test']);
};
<commit_msg>Grunt watch task fix<commit_after>/*jshint camelcase: false */

'use strict';

module.exports = function (grunt) {

  var app = {
    files: {
      js: [
        'Gruntfile.js',
        'generators/{,**/}*.js',
        'test/{,**/}*.js'
      ]
    }
  };

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

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      gruntfile: {
        files: ['Gruntfile.js']
      },
      js: {
        files: app.files.js,
        tasks: ['jshint', 'jscs', 'mocha']
      }
    },

    // JavaScript Lint
    jshint: {
      options: {
        jshintrc: true,
        reporter: require('jshint-stylish')
      },
      all: app.files.js
    },

    // JavaScript Code Style check
    jscs: {
      all: app.files.js
    },

    shell: {
      options: {
        stdout: true,
        stderr: false
      },
      test: {
        command: 'npm test'
      }
    },

    mochaTest: {
      test: {
        options: {
          reporter: 'spec'
        },
        src: 'test/index.js'
      }
    },

    mocha_istanbul: {
      coverage: {
        src: 'test/index.js',
        options: {
          quiet: true
        }
      },
      coveralls: {
        src: 'test/index.js',
        options: {
          quiet: true,
          coverage:true,
          check: {
            lines: 75,
            statements: 75
          },
          root: 'app',
          reportFormats: ['cobertura','lcovonly']
        }
      }
    },

    clean: {
      temp: ['.tmp'],
      coverage: ['test/coverage.html']
    }
  });

  grunt.event.on('coverage', function (lcov, done) {
    require('coveralls').handleInput(lcov, function (err) {
      if (err) {
        return done(err);
      }
      done();
    });
  });

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

  grunt.registerTask('mocha', ['mochaTest:test']);
  grunt.registerTask('coverage', ['mocha_istanbul:coverage']);
  grunt.registerTask('coveralls', ['mocha_istanbul:coveralls']);

  grunt.registerTask('test', ['clean', 'jshint', 'jscs', 'coverage', 'mocha']);
  grunt.registerTask('travis', ['jshint', 'jscs', 'coverage', 'coveralls', 'mocha']);
  grunt.registerTask('default', ['test']);
};
<|endoftext|>


<commit_before>'use strict';
const { json } = require('body-parser');
const express = require('express');
const app = express();
const mongoose = require('mongoose');
app.use(json());
mongoose.Promise = Promise;

const Person = require('../models/personMdl');
const Household = require('../models/householdMdl')

module.exports.addPerson = ({body}, res, err) => {
	// console.log(body)
	Person
	.create(body)
	.then((data) => {
		res.json(data)
		// let people = [];
		// people.push(data._id)
		// console.log(data)
		// Household
		// .find(
		// 	{id: data.householdId})
		// .populate({peopleArray: data._id})
		// .then((data) => {
		// 	console.log(data, "then")
		// 	res.json(data)
		})
		.catch(err)
	}
// const popPersonArray = (personId) => {
// Household
// .find()
// .populate({
// 	path: 'person',
// 	match: { }
// })
// }

module.exports.getAllPersons = (req, res, err) => {
	Person
	.find()
	.then((person) => {
		res.json(person)
	})
}

module.exports.addResource = ({body}, res, err) => {
	Person
	.findOneAndUpdate(
		{_id: body.id},
		{resourceType: body.resourceType,
		resourceAmount: body.resourceAmount},
		{upsert: true}
	)
	.then((data) => {
		res.json(data)
	})
	.catch(err)
}

module.exports.addIncome = ({body}, res, err) => {
	Person
	.findOneAndUpdate(
		{_id: body.id},
		{employerName: body.employerName,
		payArray: body.payArray},
		{upsert: true}
	)
	.then((data) => {
		res.json(data)
	})
	.catch(err)
}

module.exports.getPersonByHousehold = ({params: {id}}, res, err) => {
	Person
	.find({householdId: id})
	.then((data) => {
		res.json(data)
	})
	.catch(err)
}
<commit_msg>route to add person now includes updating household personarray with personId<commit_after>'use strict';
const { json } = require('body-parser');
const express = require('express');
const app = express();
const mongoose = require('mongoose');
app.use(json());
mongoose.Promise = Promise;

const Person = require('../models/personMdl');
const Household = require('../models/householdMdl')

module.exports.addPerson = ({body}, res, err) => {
	// console.log(body)
	Person
	.create(body)
	.then((data) => {
		// res.json(data)
		let people = [];
		people.push(data._id) //ADDING NEW PERSON ID TO ARRAY
		// console.log(data)
		Household
		.findOneAndUpdate(
			{_id: data.householdId},
			{$push: {peopleArray: data._id}},
			{new: true})
		.then((data) => {
			console.log(data, "then")
			res.json(data)
		})
		.catch(err)
	})
}
// const popPersonArray = (personId) => {
// Household
// .find()
// .populate({
// 	path: 'person',
// 	match: { }
// })
// }

module.exports.getAllPersons = (req, res, err) => {
	Person
	.find()
	.then((person) => {
		res.json(person)
	})
}

module.exports.addResource = ({body}, res, err) => {
	Person
	.findOneAndUpdate(
		{_id: body.id},
		{resourceType: body.resourceType,
		resourceAmount: body.resourceAmount},
		{upsert: true}
	)
	.then((data) => {
		res.json(data)
	})
	.catch(err)
}

module.exports.addIncome = ({body}, res, err) => {
	Person
	.findOneAndUpdate(
		{_id: body.id},
		{employerName: body.employerName,
		payArray: body.payArray},
		{upsert: true}
	)
	.then((data) => {
		res.json(data)
	})
	.catch(err)
}

module.exports.getPersonByHousehold = ({params: {id}}, res, err) => {
	Person
	.find({householdId: id})
	.then((data) => {
		res.json(data)
	})
	.catch(err)
}
<|endoftext|>


<commit_before>export function append(target, node) {
	target.appendChild(node);
}

export function insert(target, node, anchor) {
	target.insertBefore(node, anchor);
}

export function detachNode(node) {
	node.parentNode.removeChild(node);
}

export function detachBetween(before, after) {
	while (before.nextSibling && before.nextSibling !== after) {
		before.parentNode.removeChild(before.nextSibling);
	}
}

export function detachBefore(after) {
	while (after.previousSibling) {
		after.parentNode.removeChild(after.previousSibling);
	}
}

export function detachAfter(before) {
	while (before.nextSibling) {
		before.parentNode.removeChild(before.nextSibling);
	}
}

export function reinsertBetween(before, after, target) {
	while (before.nextSibling && before.nextSibling !== after) {
		target.appendChild(before.parentNode.removeChild(before.nextSibling));
	}
}

export function reinsertChildren(parent, target) {
	while (parent.firstChild) target.appendChild(parent.firstChild);
}

export function reinsertAfter(before, target) {
	while (before.nextSibling) target.appendChild(before.nextSibling);
}

export function reinsertBefore(after, target) {
	var parent = after.parentNode;
	while (parent.firstChild !== after) target.appendChild(parent.firstChild);
}

export function destroyEach(iterations, detach) {
	for (var i = 0; i < iterations.length; i += 1) {
		if (iterations[i]) iterations[i].d(detach);
	}
}

export function createFragment() {
	return document.createDocumentFragment();
}

export function createElement(name) {
	return document.createElement(name);
}

export function createSvgElement(name) {
	return document.createElementNS('http://www.w3.org/2000/svg', name);
}

export function createText(data) {
	return document.createTextNode(data);
}

export function createComment() {
	return document.createComment('');
}

export function addListener(node, event, handler) {
	node.addEventListener(event, handler, false);
}

export function removeListener(node, event, handler) {
	node.removeEventListener(event, handler, false);
}

export function setAttribute(node, attribute, value) {
	node.setAttribute(attribute, value);
}

export function setAttributes(node, attributes) {
	for (var key in attributes) {
		if (key in node) {
			node[key] = attributes[key];
		} else {
			if (attributes[key] === undefined) removeAttribute(node, key);
			else setAttribute(node, key, attributes[key]);
		}
	}
}

export function setCustomElementData(node, prop, value) {
	if (prop in node) {
		node[prop] = value;
	} else if (value) {
		setAttribute(node, prop, value);
	} else {
		removeAttribute(node, prop);
	}
}

export function removeAttribute(node, attribute) {
	node.removeAttribute(attribute);
}

export function setXlinkAttribute(node, attribute, value) {
	node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);
}

export function getBindingGroupValue(group) {
	var value = [];
	for (var i = 0; i < group.length; i += 1) {
		if (group[i].checked) value.push(group[i].__value);
	}
	return value;
}

export function toNumber(value) {
	return value === '' ? undefined : +value;
}

export function timeRangesToArray(ranges) {
	var array = [];
	for (var i = 0; i < ranges.length; i += 1) {
		array.push({ start: ranges.start(i), end: ranges.end(i) });
	}
	return array;
}

export function children (element) {
	return Array.from(element.childNodes);
}

export function claimElement (nodes, name, attributes, svg) {
	for (var i = 0; i < nodes.length; i += 1) {
		var node = nodes[i];
		if (node.nodeName === name) {
			for (var j = 0; j < node.attributes.length; j += 1) {
				var attribute = node.attributes[j];
				if (!attributes[attribute.name]) node.removeAttribute(attribute.name);
			}
			return nodes.splice(i, 1)[0]; // TODO strip unwanted attributes
		}
	}

	return svg ? createSvgElement(name) : createElement(name);
}

export function claimText (nodes, data) {
	for (var i = 0; i < nodes.length; i += 1) {
		var node = nodes[i];
		if (node.nodeType === 3) {
			node.data = data;
			return nodes.splice(i, 1)[0];
		}
	}

	return createText(data);
}

export function setData(text, data) {
	text.data = '' + data;
}

export function setInputType(input, type) {
	try {
		input.type = type;
	} catch (e) {}
}

export function setStyle(node, key, value) {
	node.style.setProperty(key, value);
}

export function selectOption(select, value) {
	for (var i = 0; i < select.options.length; i += 1) {
		var option = select.options[i];

		if (option.__value === value) {
			option.selected = true;
			return;
		}
	}
}

export function selectOptions(select, value) {
	for (var i = 0; i < select.options.length; i += 1) {
		var option = select.options[i];
		option.selected = ~value.indexOf(option.__value);
	}
}

export function selectValue(select) {
	var selectedOption = select.querySelector(':checked') || select.options[0];
	return selectedOption && selectedOption.__value;
}

export function selectMultipleValue(select) {
	return [].map.call(select.querySelectorAll(':checked'), function(option) {
		return option.__value;
	});
}

export function addResizeListener(element, fn) {
	if (getComputedStyle(element).position === 'static') {
		element.style.position = 'relative';
	}

	const object = document.createElement('object');
	object.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
	object.type = 'text/html';

	let win;

	object.onload = () => {
		win = object.contentDocument.defaultView;
		win.addEventListener('resize', fn);
	};

	if (/Trident/.test(navigator.userAgent)) {
		element.appendChild(object);
		object.data = 'about:blank';
	} else {
		object.data = 'about:blank';
		element.appendChild(object);
	}

	return {
		cancel: () => {
			win && win.removeEventListener('resize', fn);
			element.removeChild(object);
		}
	};
}<commit_msg>fix resize listener bug for ios re: #1600<commit_after>export function append(target, node) {
	target.appendChild(node);
}

export function insert(target, node, anchor) {
	target.insertBefore(node, anchor);
}

export function detachNode(node) {
	node.parentNode.removeChild(node);
}

export function detachBetween(before, after) {
	while (before.nextSibling && before.nextSibling !== after) {
		before.parentNode.removeChild(before.nextSibling);
	}
}

export function detachBefore(after) {
	while (after.previousSibling) {
		after.parentNode.removeChild(after.previousSibling);
	}
}

export function detachAfter(before) {
	while (before.nextSibling) {
		before.parentNode.removeChild(before.nextSibling);
	}
}

export function reinsertBetween(before, after, target) {
	while (before.nextSibling && before.nextSibling !== after) {
		target.appendChild(before.parentNode.removeChild(before.nextSibling));
	}
}

export function reinsertChildren(parent, target) {
	while (parent.firstChild) target.appendChild(parent.firstChild);
}

export function reinsertAfter(before, target) {
	while (before.nextSibling) target.appendChild(before.nextSibling);
}

export function reinsertBefore(after, target) {
	var parent = after.parentNode;
	while (parent.firstChild !== after) target.appendChild(parent.firstChild);
}

export function destroyEach(iterations, detach) {
	for (var i = 0; i < iterations.length; i += 1) {
		if (iterations[i]) iterations[i].d(detach);
	}
}

export function createFragment() {
	return document.createDocumentFragment();
}

export function createElement(name) {
	return document.createElement(name);
}

export function createSvgElement(name) {
	return document.createElementNS('http://www.w3.org/2000/svg', name);
}

export function createText(data) {
	return document.createTextNode(data);
}

export function createComment() {
	return document.createComment('');
}

export function addListener(node, event, handler) {
	node.addEventListener(event, handler, false);
}

export function removeListener(node, event, handler) {
	node.removeEventListener(event, handler, false);
}

export function setAttribute(node, attribute, value) {
	node.setAttribute(attribute, value);
}

export function setAttributes(node, attributes) {
	for (var key in attributes) {
		if (key in node) {
			node[key] = attributes[key];
		} else {
			if (attributes[key] === undefined) removeAttribute(node, key);
			else setAttribute(node, key, attributes[key]);
		}
	}
}

export function setCustomElementData(node, prop, value) {
	if (prop in node) {
		node[prop] = value;
	} else if (value) {
		setAttribute(node, prop, value);
	} else {
		removeAttribute(node, prop);
	}
}

export function removeAttribute(node, attribute) {
	node.removeAttribute(attribute);
}

export function setXlinkAttribute(node, attribute, value) {
	node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);
}

export function getBindingGroupValue(group) {
	var value = [];
	for (var i = 0; i < group.length; i += 1) {
		if (group[i].checked) value.push(group[i].__value);
	}
	return value;
}

export function toNumber(value) {
	return value === '' ? undefined : +value;
}

export function timeRangesToArray(ranges) {
	var array = [];
	for (var i = 0; i < ranges.length; i += 1) {
		array.push({ start: ranges.start(i), end: ranges.end(i) });
	}
	return array;
}

export function children (element) {
	return Array.from(element.childNodes);
}

export function claimElement (nodes, name, attributes, svg) {
	for (var i = 0; i < nodes.length; i += 1) {
		var node = nodes[i];
		if (node.nodeName === name) {
			for (var j = 0; j < node.attributes.length; j += 1) {
				var attribute = node.attributes[j];
				if (!attributes[attribute.name]) node.removeAttribute(attribute.name);
			}
			return nodes.splice(i, 1)[0]; // TODO strip unwanted attributes
		}
	}

	return svg ? createSvgElement(name) : createElement(name);
}

export function claimText (nodes, data) {
	for (var i = 0; i < nodes.length; i += 1) {
		var node = nodes[i];
		if (node.nodeType === 3) {
			node.data = data;
			return nodes.splice(i, 1)[0];
		}
	}

	return createText(data);
}

export function setData(text, data) {
	text.data = '' + data;
}

export function setInputType(input, type) {
	try {
		input.type = type;
	} catch (e) {}
}

export function setStyle(node, key, value) {
	node.style.setProperty(key, value);
}

export function selectOption(select, value) {
	for (var i = 0; i < select.options.length; i += 1) {
		var option = select.options[i];

		if (option.__value === value) {
			option.selected = true;
			return;
		}
	}
}

export function selectOptions(select, value) {
	for (var i = 0; i < select.options.length; i += 1) {
		var option = select.options[i];
		option.selected = ~value.indexOf(option.__value);
	}
}

export function selectValue(select) {
	var selectedOption = select.querySelector(':checked') || select.options[0];
	return selectedOption && selectedOption.__value;
}

export function selectMultipleValue(select) {
	return [].map.call(select.querySelectorAll(':checked'), function(option) {
		return option.__value;
	});
}

export function addResizeListener(element, fn) {
	if (getComputedStyle(element).position === 'static') {
		element.style.position = 'relative';
	}

	const object = document.createElement('object');
	object.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
	object.type = 'text/html';

	let win;

	object.onload = () => {
		win = object.contentDocument.defaultView;
		win.addEventListener('resize', fn);
	};

	if (/Trident/.test(navigator.userAgent)) {
		element.appendChild(object);
		object.data = 'about:blank';
	} else {
		object.data = 'about:blank';
		element.appendChild(object);
	}

	return {
		cancel: () => {
			win && win.removeEventListener && win.removeEventListener('resize', fn);
			element.removeChild(object);
		}
	};
}<|endoftext|>


<commit_before>var Chart = require('./core/core.js')();

require('./core/core.helpers')(Chart);
require('./core/core.element')(Chart);
require('./core/core.animation')(Chart);
require('./core/core.controller')(Chart);
require('./core/core.datasetController')(Chart);
require('./core/core.layoutService')(Chart);
require('./core/core.scaleService')(Chart);
require('./core/core.plugin.js')(Chart);
require('./core/core.scale')(Chart);
require('./core/core.title')(Chart);
require('./core/core.legend')(Chart);
require('./core/core.tooltip')(Chart);

require('./elements/element.arc')(Chart);
require('./elements/element.line')(Chart);
require('./elements/element.point')(Chart);
require('./elements/element.rectangle')(Chart);

require('./scales/scale.linearbase.js')(Chart);
require('./scales/scale.category')(Chart);
require('./scales/scale.linear')(Chart);
require('./scales/scale.logarithmic')(Chart);
require('./scales/scale.radialLinear')(Chart);
require('./scales/scale.time')(Chart);

// Controllers must be loaded after elements
// See Chart.core.datasetController.dataElementType
require('./controllers/controller.bar')(Chart);
require('./controllers/controller.bubble')(Chart);
require('./controllers/controller.doughnut')(Chart);
require('./controllers/controller.line')(Chart);
require('./controllers/controller.polarArea')(Chart);
require('./controllers/controller.radar')(Chart);

require('./charts/Chart.Bar')(Chart);
require('./charts/Chart.Bubble')(Chart);
require('./charts/Chart.Doughnut')(Chart);
require('./charts/Chart.Line')(Chart);
require('./charts/Chart.PolarArea')(Chart);
require('./charts/Chart.Radar')(Chart);
require('./charts/Chart.Scatter')(Chart);

window.Chart = module.exports = Chart;
<commit_msg>Comment out modules I don't need,<commit_after>var Chart = require('./core/core.js')();

require('./core/core.helpers')(Chart);
require('./core/core.element')(Chart);
require('./core/core.animation')(Chart);
require('./core/core.controller')(Chart);
require('./core/core.datasetController')(Chart);
require('./core/core.layoutService')(Chart);
require('./core/core.scaleService')(Chart);
require('./core/core.plugin.js')(Chart);
require('./core/core.scale')(Chart);
require('./core/core.title')(Chart);
require('./core/core.legend')(Chart);
require('./core/core.tooltip')(Chart);

require('./elements/element.arc')(Chart);
require('./elements/element.line')(Chart);
require('./elements/element.point')(Chart);
require('./elements/element.rectangle')(Chart);

require('./scales/scale.linearbase.js')(Chart);
require('./scales/scale.category')(Chart);
require('./scales/scale.linear')(Chart);
//require('./scales/scale.logarithmic')(Chart);
//require('./scales/scale.radialLinear')(Chart);
require('./scales/scale.time')(Chart);

// Controllers must be loaded after elements
// See Chart.core.datasetController.dataElementType
require('./controllers/controller.bar')(Chart);
//require('./controllers/controller.bubble')(Chart);
require('./controllers/controller.doughnut')(Chart);
require('./controllers/controller.line')(Chart);
//require('./controllers/controller.polarArea')(Chart);
//require('./controllers/controller.radar')(Chart);

require('./charts/Chart.Bar')(Chart);
//require('./charts/Chart.Bubble')(Chart);
require('./charts/Chart.Doughnut')(Chart);
require('./charts/Chart.Line')(Chart);
//require('./charts/Chart.PolarArea')(Chart);
//require('./charts/Chart.Radar')(Chart);
//require('./charts/Chart.Scatter')(Chart);

window.Chart = module.exports = Chart;
<|endoftext|>


<commit_before>/*
 * nodesearch.js
 *
 * file search utility written in node.js
 */

var assert = require('assert');
var fs = require('fs');
var path = require('path');

var FileUtil = require('./fileutil.js').FileUtil;
var SearchResult = require('./searchresult.js').SearchResult;

// add a startsWith method to String type
if (typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function (str) {
        return this.slice(0, str.length) == str;
    };
}

// add a format method to String type (use {0} style placeholders)
// from http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format/4673436#4673436
if (!String.prototype.format) {
  String.prototype.format = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

function Searcher(settings) {
    var that = this;
    var _settings = settings;
    var _fileutil = new FileUtil();
    var _timers = {};
    this.results = [];

    var log = function (message) {
        console.log(message);
    }

    var validateSettings = function () {
        assert.ok(_settings.startPath, 'Startpath not defined');
        assert.ok(fs.existsSync(_settings.startPath), 'Startpath not found');
        assert.ok(isSearchDir(_settings.startPath), 'Startpath does not match search settings');
        assert.ok(_settings.searchPatterns.length, 'No search patterns specified');
    };

    var matchesAnyElement = function (s, elements) {
        return elements.indexOf(s) > -1;
    };

    var matchesAnyPattern = function (s, patterns) {
        for (var p in patterns) {
            var pattern = patterns[p];
            var match = pattern.exec(s);
            if (match) {
                return true;
            }
        }
        return false;
    };

    var isSearchDir = function (dir) {
        var pathElems = dir.split(path.sep);
        if (_settings.excludeHidden) {
            for (var p in pathElems) {
                if (!matchesAnyElement(pathElems[p], ['.','..']) &&
                    pathElems[p].startsWith('.')) {
                    return false;
                }
            }
        }
        if (_settings.inDirPatterns.length && !matchesAnyPattern(dir,
            _settings.inDirPatterns)) {
            return false;
        }
        if (_settings.outDirPatterns.length && matchesAnyPattern(dir,
            _settings.outDirPatterns)) {
            return false;
        }
        return true;
    };
    // can validate now that isSearchDir is defined
    validateSettings();

    var isSearchFile = function (file) {
        if (file.startsWith(".") && _settings.excludeHidden) {
            return false;
        }
        if (_settings.inExtensions.length &&
            !matchesAnyElement(_fileutil.getExtension(file), _settings.inExtensions)) {
            return false;
        }
        if (_settings.outExtensions.length &&
            matchesAnyElement(_fileutil.getExtension(file), _settings.outExtensions)) {
            return false;
        }
        if (_settings.inFilePatterns.length &&
            !matchesAnyPattern(file, _settings.inFilePatterns)) {
            return false;
        }
        if (_settings.outFilePatterns.length &&
            matchesAnyPattern(file, _settings.outFilePatterns)) {
            return false;
        }
        return true;
    };

    var isArchiveSearchFile = function (file) {
        if (file.startsWith(".") && _settings.excludeHidden) {
            return false;
        }
        if (_settings.inArchiveExtensions.length &&
            !matchesAnyElement(_fileutil.getExtension(file), _settings.inArchiveExtensions)) {
            return false;
        }
        if (_settings.outArchiveExtensions.length &&
            matchesAnyElement(_fileutil.getExtension(file), _settings.outArchiveExtensions)) {
            return false;
        }
        if (_settings.inArchiveFilePatterns.length &&
            !matchesAnyPattern(file, _settings.inArchiveFilePatterns)) {
            return false;
        }
        if (_settings.outArchiveFilePatterns.length &&
            matchesAnyPattern(file, _settings.outArchiveFilePatterns)) {
            return false;
        }
        return true;
    };

    var getSearchDirs = function (startPath) {
        if (_settings.debug) {
            log("Getting list of directories to search under " + startPath);
        }
        var searchDirs = [];
        if (_settings.recursive) {
            searchDirs.push.apply(searchDirs, recGetSearchDirs(startPath));
        }
        return searchDirs;
    }

    var handleFsError = function (err) {
        if (err.errno === 34 && err.code === "ENOENT") {
            // this error seems to occur when the file is a soft link
            // to a non-existent file
        } else {
            log(err);
            process.exit(1);
        }
    }

    var getSubDirs = function (dir) {
        var subDirs = [];
        var childItems = fs.readdirSync(dir);
        for (var c in childItems) {
            var filepath = path.join(dir, childItems[c]);
            try {
                var stats = fs.statSync(filepath);
                if (stats.isDirectory()) {
                    subDirs.push(filepath);
                }
            } catch (err) {
                handleFsError(err);
            }
        }
        return subDirs;
    };

    var recGetSearchDirs = function (currentDir) {
        var searchDirs = [];
        var subDirs = getSubDirs(currentDir);
        for (var d in subDirs) {
            if (isSearchDir(subDirs[d])) {
                searchDirs.push(subDirs[d]);
            }
        }
        for (var d in subDirs) {
            searchDirs.push.apply(searchDirs, recGetSearchDirs(subDirs[d]));
        }
        return searchDirs;
    };

    var getFilesForDirectory = function (dir) {
        var files = [];
        var childItems = fs.readdirSync(dir);
        for (var c in childItems) {
            var filepath = path.join(dir, childItems[c]);
            try {
                var stats = fs.statSync(filepath);
                if (stats.isFile()) {
                    files.push(filepath);
                }
            } catch (err) {
                handleFsError(err);
            }
        }
        return files;
    }

    var getSearchFilesForDirectory = function (dir) {
        var searchFiles = [];
        var dirFiles = getFilesForDirectory(dir);
        for (var d in dirFiles) {
            var f = dirFiles[d];
            if (_fileutil.isArchiveFile(f) && _settings.searchArchives &&
                isArchiveSearchFile(f)) {
                searchFiles.push(f);
            } else if (!_settings.archivesOnly && isSearchFile(f)) {
                searchFiles.push(f);
            }
        }
        return searchFiles;
    }

    var getSearchFiles = function (searchDirs) {
        var searchFiles = [];
        for (var d in searchDirs) {
            searchFiles.push.apply(searchFiles, getSearchFilesForDirectory(searchDirs[d]));
        }
        return searchFiles;
    };

    var addTimer = function (name, action) {
        _timers[name + ":" + action] = new Date();
    };

    var startTimer = function (name) {
        addTimer(name, "start");
    };

    var stopTimer = function (name) {
        addTimer(name, "stop");
        if (_settings.printResults)
            printElapsed(name);
    };

    var getElapsed = function (name) {
        var start = _timers[name+":start"];
        var stop = _timers[name+":stop"];
        return stop - start;
    };

    var printElapsed = function (name) {
        var elapsed = getElapsed(name);
        log("Elapsed time for " + name + ": " + elapsed + " milliseconds");
    };

    this.search = function () {
        if (_settings.verbose)
            log("Search initiated");

        // get the search dirs
        if (_settings.doTiming)
            startTimer("GetSearchDirs");
        var dirs = [_settings.startPath];
        dirs.push.apply(dirs, getSearchDirs(_settings.startPath));
        if (_settings.doTiming)
            stopTimer("GetSearchDirs");
        if (_settings.verbose) {
            log("\nDirectories to be searched (" + dirs.length + "):");
            for (var d in dirs) {
                log(dirs[d]);
            }
        }

        // get the search files
        if (_settings.doTiming)
            startTimer("GetSearchFiles");
        var files = getSearchFiles(dirs);
        if (_settings.doTiming)
            stopTimer("GetSearchFiles");
        if (_settings.verbose) {
            log("\nFiles to be searched (" + files.length + "):");
            for (var f in files) {
                log(files[f]);
            }
            log("");
        }

        if (_settings.doTiming)
            startTimer("SearchFiles");
        for (var i in files) {
            searchFile(files[i]);
        }
        if (_settings.doTiming)
            stopTimer("SearchFiles");

        if (_settings.verbose)
            log("Search complete.");
    };

    var searchFile = function (filepath) {
        if (_fileutil.isTextFile(filepath)) {
            searchTextFile(filepath);
        } else if (_fileutil.isBinaryFile(filepath)) {
            searchBinaryFile(filepath);
        }
    };

    var searchBinaryFile = function (filepath) {
        if (_settings.verbose) {
            log('Searching binary file: "'+filepath+'"');
        }
        var contents = fs.readFileSync(filepath).toString();
        var pattern = '';
        for (p in _settings.searchPatterns) {
            pattern = _settings.searchPatterns[p];
            var match = pattern.exec(contents);
            if (match) {
                addSearchResult(new SearchResult(pattern, filepath, 0, 0, 0,
                    null, [], []));
            }
        }
    };

    var searchTextFile = function (filepath) {
        if (_settings.verbose) {
            log('Searching text file: "'+filepath+'"');
        }
        if (_settings.multilineSearch)
            searchTextFileContents(filepath);
        else
            searchTextFileLines(filepath);
    };

    var getLineCount = function (contents) {
        var lineCount = 0;
        if (contents) {
            var matches = contents.match(/(\r?\n)/g);
            if (matches) lineCount = matches.length;
        }
        return lineCount;
    };

    var searchTextFileContents = function (filepath) {
        var contents = fs.readFileSync(filepath).toString();
        var results = searchContents(contents);
        for (i in results) {
            var r = results[i];
            var resultWithFilepath =
                new SearchResult(r.pattern, filepath, r.linenum,
                        r.matchStartIndex, r.matchEndIndex, r.line,
                        r.linesBefore, r.linesAfter);
            addSearchResult(resultWithFilepath);
        }
    }

    var searchContents = function (contents) {
        var patternResults = {};
        var results = [];
        for (p in _settings.searchPatterns) {
            var pattern = new RegExp(_settings.searchPatterns[p].source, "g");
            if (_settings.firstMatch && pattern in patternResults)
                continue;
            var match = pattern.exec(contents);
            while (match) {
                var beforeLineCount = 0;
                if (match.index) {
                    var beforeContents = contents.substring(0, match.index);
                    beforeLineCount = getLineCount(beforeContents);
                }
                var afterLineCount = 0;
                if (pattern.lastIndex < contents.length) {
                    var afterContents = contents.substring(pattern.lastIndex);
                    afterLineCount = getLineCount(afterContents);
                }
                var lineStartIndex = match.index;
                while (lineStartIndex > 0 &&
                       contents.charAt(lineStartIndex) != '\n')
                    lineStartIndex -= 1;
                var lineEndIndex = pattern.lastIndex;
                while (lineEndIndex < contents.length &&
                       contents.charAt(lineEndIndex) != '\n')
                    lineEndIndex += 1;
                line = contents.substring(lineStartIndex, lineEndIndex);
                var searchResult = new SearchResult(pattern, '',
                    beforeLineCount+1, 0, 0, line, [], []);
                results.push(searchResult);
                if (!(pattern in patternResults))
                    patternResults[pattern] = [];
                patternResults[pattern].push(searchResult);
                match = pattern.exec(contents);
            }
        }
        return results;
    };

    var searchTextFileLines = function (filepath) {
        var contents = fs.readFileSync(filepath).toString();
        var lines = contents.toString().split(/\r?\n/);
        var results = searchLines(lines);
        for (i in results) {
            var r = results[i];
            var resultWithFilepath =
                new SearchResult(r.pattern, filepath, r.linenum,
                        r.matchStartIndex, r.matchEndIndex, r.line,
                        r.linesBefore, r.linesAfter);
            addSearchResult(resultWithFilepath);
        }
    };

    // return results so that filepath can be added to them
    var searchLines = function (lines) {
        var linenum = 0;
        var pattern;
        var results = [];
        for (i in lines) {
            linenum += 1;
            for (p in _settings.searchPatterns) {
                pattern = new RegExp(_settings.searchPatterns[p].source, "g");
                var match = pattern.exec(lines[i]);
                while (match) {
                    results.push(new SearchResult(pattern, '', linenum,
                        match.index+1, pattern.lastIndex+1, lines[i], [], []));
                    if (_settings.firstMatch) {
                        return;
                    }
                    match = pattern.exec(lines[i]);
                }
            }
        }
        return results;
    };

    var addSearchResult = function (result) {
        that.results.push(result);
    };

    var boolHashFromArray = function (arr) {
        var hash = {};
        for (var a in arr) {
            hash[arr[a]] = true;
        }
        return hash;
    };

    var setFromArray = function (arr) {
        var hash = boolHashFromArray(arr);
        var set = [];
        for (var h in hash) {
            set.push(h);
        }
        return set;
    };

    this.getMatchingDirs = function () {
        var dirs = [];
        for (var r in that.results) {
            var result = that.results[r];
            dirs.push(path.dirname(result.filename));
        }
        return setFromArray(dirs);
    };

    this.printMatchingDirs = function () {
        var dirs = that.getMatchingDirs();
        log("\nDirectories with matches ("+dirs.length+"):");
        for (var d in dirs) {
            log(dirs[d]);
        }
    };

    this.getMatchingFiles = function () {
        var files = [];
        for (var r in that.results) {
            var result = that.results[r];
            files.push(result.filename);
        }
        return setFromArray(files);
    };

    this.printMatchingFiles = function () {
        var files = that.getMatchingFiles();
        log("\nFiles with matches ("+files.length+"):");
        for (var f in files) {
            log(files[f]);
        }
    };

    this.getMatchingLines = function () {
        var lines = [];
        for (var r in that.results) {
            var result = that.results[r];
            if (result.linenum)
                lines.push(result.line);
        }
        return setFromArray(lines);
    };

    this.printMatchingLines = function () {
        var lines = that.getMatchingLines();
        log("\nLines with matches ("+lines.length+"):");
        for (var l in lines) {
            log(lines[l]);
        }
    };
}

exports.Searcher = Searcher;<commit_msg>Fixed uniqueLines behavior in node<commit_after>/*
 * nodesearch.js
 *
 * file search utility written in node.js
 */

var assert = require('assert');
var fs = require('fs');
var path = require('path');

var FileUtil = require('./fileutil.js').FileUtil;
var SearchResult = require('./searchresult.js').SearchResult;

// add a startsWith method to String type
if (typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function (str) {
        return this.slice(0, str.length) == str;
    };
}

// add a format method to String type (use {0} style placeholders)
// from http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format/4673436#4673436
if (!String.prototype.format) {
  String.prototype.format = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

function Searcher(settings) {
    var that = this;
    var _settings = settings;
    var _fileutil = new FileUtil();
    var _timers = {};
    this.results = [];

    var log = function (message) {
        console.log(message);
    }

    var validateSettings = function () {
        assert.ok(_settings.startPath, 'Startpath not defined');
        assert.ok(fs.existsSync(_settings.startPath), 'Startpath not found');
        assert.ok(isSearchDir(_settings.startPath), 'Startpath does not match search settings');
        assert.ok(_settings.searchPatterns.length, 'No search patterns specified');
    };

    var matchesAnyElement = function (s, elements) {
        return elements.indexOf(s) > -1;
    };

    var matchesAnyPattern = function (s, patterns) {
        for (var p in patterns) {
            var pattern = patterns[p];
            var match = pattern.exec(s);
            if (match) {
                return true;
            }
        }
        return false;
    };

    var isSearchDir = function (dir) {
        var pathElems = dir.split(path.sep);
        if (_settings.excludeHidden) {
            for (var p in pathElems) {
                if (!matchesAnyElement(pathElems[p], ['.','..']) &&
                    pathElems[p].startsWith('.')) {
                    return false;
                }
            }
        }
        if (_settings.inDirPatterns.length && !matchesAnyPattern(dir,
            _settings.inDirPatterns)) {
            return false;
        }
        if (_settings.outDirPatterns.length && matchesAnyPattern(dir,
            _settings.outDirPatterns)) {
            return false;
        }
        return true;
    };
    // can validate now that isSearchDir is defined
    validateSettings();

    var isSearchFile = function (file) {
        if (file.startsWith(".") && _settings.excludeHidden) {
            return false;
        }
        if (_settings.inExtensions.length &&
            !matchesAnyElement(_fileutil.getExtension(file), _settings.inExtensions)) {
            return false;
        }
        if (_settings.outExtensions.length &&
            matchesAnyElement(_fileutil.getExtension(file), _settings.outExtensions)) {
            return false;
        }
        if (_settings.inFilePatterns.length &&
            !matchesAnyPattern(file, _settings.inFilePatterns)) {
            return false;
        }
        if (_settings.outFilePatterns.length &&
            matchesAnyPattern(file, _settings.outFilePatterns)) {
            return false;
        }
        return true;
    };

    var isArchiveSearchFile = function (file) {
        if (file.startsWith(".") && _settings.excludeHidden) {
            return false;
        }
        if (_settings.inArchiveExtensions.length &&
            !matchesAnyElement(_fileutil.getExtension(file), _settings.inArchiveExtensions)) {
            return false;
        }
        if (_settings.outArchiveExtensions.length &&
            matchesAnyElement(_fileutil.getExtension(file), _settings.outArchiveExtensions)) {
            return false;
        }
        if (_settings.inArchiveFilePatterns.length &&
            !matchesAnyPattern(file, _settings.inArchiveFilePatterns)) {
            return false;
        }
        if (_settings.outArchiveFilePatterns.length &&
            matchesAnyPattern(file, _settings.outArchiveFilePatterns)) {
            return false;
        }
        return true;
    };

    var getSearchDirs = function (startPath) {
        if (_settings.debug) {
            log("Getting list of directories to search under " + startPath);
        }
        var searchDirs = [];
        if (_settings.recursive) {
            searchDirs.push.apply(searchDirs, recGetSearchDirs(startPath));
        }
        return searchDirs;
    }

    var handleFsError = function (err) {
        if (err.errno === 34 && err.code === "ENOENT") {
            // this error seems to occur when the file is a soft link
            // to a non-existent file
        } else {
            log(err);
            process.exit(1);
        }
    }

    var getSubDirs = function (dir) {
        var subDirs = [];
        var childItems = fs.readdirSync(dir);
        for (var c in childItems) {
            var filepath = path.join(dir, childItems[c]);
            try {
                var stats = fs.statSync(filepath);
                if (stats.isDirectory()) {
                    subDirs.push(filepath);
                }
            } catch (err) {
                handleFsError(err);
            }
        }
        return subDirs;
    };

    var recGetSearchDirs = function (currentDir) {
        var searchDirs = [];
        var subDirs = getSubDirs(currentDir);
        for (var d in subDirs) {
            if (isSearchDir(subDirs[d])) {
                searchDirs.push(subDirs[d]);
            }
        }
        for (var d in subDirs) {
            searchDirs.push.apply(searchDirs, recGetSearchDirs(subDirs[d]));
        }
        return searchDirs;
    };

    var getFilesForDirectory = function (dir) {
        var files = [];
        var childItems = fs.readdirSync(dir);
        for (var c in childItems) {
            var filepath = path.join(dir, childItems[c]);
            try {
                var stats = fs.statSync(filepath);
                if (stats.isFile()) {
                    files.push(filepath);
                }
            } catch (err) {
                handleFsError(err);
            }
        }
        return files;
    }

    var getSearchFilesForDirectory = function (dir) {
        var searchFiles = [];
        var dirFiles = getFilesForDirectory(dir);
        for (var d in dirFiles) {
            var f = dirFiles[d];
            if (_fileutil.isArchiveFile(f) && _settings.searchArchives &&
                isArchiveSearchFile(f)) {
                searchFiles.push(f);
            } else if (!_settings.archivesOnly && isSearchFile(f)) {
                searchFiles.push(f);
            }
        }
        return searchFiles;
    }

    var getSearchFiles = function (searchDirs) {
        var searchFiles = [];
        for (var d in searchDirs) {
            searchFiles.push.apply(searchFiles, getSearchFilesForDirectory(searchDirs[d]));
        }
        return searchFiles;
    };

    var addTimer = function (name, action) {
        _timers[name + ":" + action] = new Date();
    };

    var startTimer = function (name) {
        addTimer(name, "start");
    };

    var stopTimer = function (name) {
        addTimer(name, "stop");
        if (_settings.printResults)
            printElapsed(name);
    };

    var getElapsed = function (name) {
        var start = _timers[name+":start"];
        var stop = _timers[name+":stop"];
        return stop - start;
    };

    var printElapsed = function (name) {
        var elapsed = getElapsed(name);
        log("Elapsed time for " + name + ": " + elapsed + " milliseconds");
    };

    this.search = function () {
        if (_settings.verbose)
            log("Search initiated");

        // get the search dirs
        if (_settings.doTiming)
            startTimer("GetSearchDirs");
        var dirs = [_settings.startPath];
        dirs.push.apply(dirs, getSearchDirs(_settings.startPath));
        if (_settings.doTiming)
            stopTimer("GetSearchDirs");
        if (_settings.verbose) {
            log("\nDirectories to be searched (" + dirs.length + "):");
            for (var d in dirs) {
                log(dirs[d]);
            }
        }

        // get the search files
        if (_settings.doTiming)
            startTimer("GetSearchFiles");
        var files = getSearchFiles(dirs);
        if (_settings.doTiming)
            stopTimer("GetSearchFiles");
        if (_settings.verbose) {
            log("\nFiles to be searched (" + files.length + "):");
            for (var f in files) {
                log(files[f]);
            }
            log("");
        }

        if (_settings.doTiming)
            startTimer("SearchFiles");
        for (var i in files) {
            searchFile(files[i]);
        }
        if (_settings.doTiming)
            stopTimer("SearchFiles");

        if (_settings.verbose)
            log("Search complete.");
    };

    var searchFile = function (filepath) {
        if (_fileutil.isTextFile(filepath)) {
            searchTextFile(filepath);
        } else if (_fileutil.isBinaryFile(filepath)) {
            searchBinaryFile(filepath);
        }
    };

    var searchBinaryFile = function (filepath) {
        if (_settings.verbose) {
            log('Searching binary file: "'+filepath+'"');
        }
        var contents = fs.readFileSync(filepath).toString();
        var pattern = '';
        for (p in _settings.searchPatterns) {
            pattern = _settings.searchPatterns[p];
            var match = pattern.exec(contents);
            if (match) {
                addSearchResult(new SearchResult(pattern, filepath, 0, 0, 0,
                    null, [], []));
            }
        }
    };

    var searchTextFile = function (filepath) {
        if (_settings.verbose) {
            log('Searching text file: "'+filepath+'"');
        }
        if (_settings.multilineSearch)
            searchTextFileContents(filepath);
        else
            searchTextFileLines(filepath);
    };

    var getLineCount = function (contents) {
        var lineCount = 0;
        if (contents) {
            var matches = contents.match(/(\r?\n)/g);
            if (matches) lineCount = matches.length;
        }
        return lineCount;
    };

    var searchTextFileContents = function (filepath) {
        var contents = fs.readFileSync(filepath).toString();
        var results = searchContents(contents);
        for (i in results) {
            var r = results[i];
            var resultWithFilepath =
                new SearchResult(r.pattern, filepath, r.linenum,
                        r.matchStartIndex, r.matchEndIndex, r.line,
                        r.linesBefore, r.linesAfter);
            addSearchResult(resultWithFilepath);
        }
    }

    var searchContents = function (contents) {
        var patternResults = {};
        var results = [];
        for (p in _settings.searchPatterns) {
            var pattern = new RegExp(_settings.searchPatterns[p].source, "g");
            if (_settings.firstMatch && pattern in patternResults)
                continue;
            var match = pattern.exec(contents);
            while (match) {
                var beforeLineCount = 0;
                if (match.index) {
                    var beforeContents = contents.substring(0, match.index);
                    beforeLineCount = getLineCount(beforeContents);
                }
                var afterLineCount = 0;
                if (pattern.lastIndex < contents.length) {
                    var afterContents = contents.substring(pattern.lastIndex);
                    afterLineCount = getLineCount(afterContents);
                }
                var lineStartIndex = match.index;
                while (lineStartIndex > 0 &&
                       contents.charAt(lineStartIndex) != '\n')
                    lineStartIndex -= 1;
                var lineEndIndex = pattern.lastIndex;
                while (lineEndIndex < contents.length &&
                       contents.charAt(lineEndIndex) != '\n')
                    lineEndIndex += 1;
                line = contents.substring(lineStartIndex, lineEndIndex);
                var searchResult = new SearchResult(pattern, '',
                    beforeLineCount+1, 0, 0, line, [], []);
                results.push(searchResult);
                if (!(pattern in patternResults))
                    patternResults[pattern] = [];
                patternResults[pattern].push(searchResult);
                match = pattern.exec(contents);
            }
        }
        return results;
    };

    var searchTextFileLines = function (filepath) {
        var contents = fs.readFileSync(filepath).toString();
        var lines = contents.toString().split(/\r?\n/);
        var results = searchLines(lines);
        for (i in results) {
            var r = results[i];
            var resultWithFilepath =
                new SearchResult(r.pattern, filepath, r.linenum,
                        r.matchStartIndex, r.matchEndIndex, r.line,
                        r.linesBefore, r.linesAfter);
            addSearchResult(resultWithFilepath);
        }
    };

    // return results so that filepath can be added to them
    var searchLines = function (lines) {
        var linenum = 0;
        var pattern;
        var results = [];
        for (i in lines) {
            linenum += 1;
            for (p in _settings.searchPatterns) {
                pattern = new RegExp(_settings.searchPatterns[p].source, "g");
                var match = pattern.exec(lines[i]);
                while (match) {
                    results.push(new SearchResult(pattern, '', linenum,
                        match.index+1, pattern.lastIndex+1, lines[i], [], []));
                    if (_settings.firstMatch) {
                        return;
                    }
                    match = pattern.exec(lines[i]);
                }
            }
        }
        return results;
    };

    var addSearchResult = function (result) {
        that.results.push(result);
    };

    var boolHashFromArray = function (arr) {
        var hash = {};
        for (var a in arr) {
            hash[arr[a]] = true;
        }
        return hash;
    };

    var setFromArray = function (arr) {
        var hash = boolHashFromArray(arr);
        var set = [];
        for (var h in hash) {
            set.push(h);
        }
        return set;
    };

    this.getMatchingDirs = function () {
        var dirs = [];
        for (var r in that.results) {
            var result = that.results[r];
            dirs.push(path.dirname(result.filename));
        }
        return setFromArray(dirs);
    };

    this.printMatchingDirs = function () {
        var dirs = that.getMatchingDirs();
        log("\nDirectories with matches ("+dirs.length+"):");
        for (var d in dirs) {
            log(dirs[d]);
        }
    };

    this.getMatchingFiles = function () {
        var files = [];
        for (var r in that.results) {
            var result = that.results[r];
            files.push(result.filename);
        }
        return setFromArray(files);
    };

    this.printMatchingFiles = function () {
        var files = that.getMatchingFiles();
        log("\nFiles with matches ("+files.length+"):");
        for (var f in files) {
            log(files[f]);
        }
    };

    this.getMatchingLines = function () {
        var lines = [];
        for (var r in that.results) {
            var result = that.results[r];
            if (result.linenum)
                lines.push(result.line.trim());
        }
        if (_settings.uniqueLines)
            lines = setFromArray(lines);
        lines.sort();
        return lines;
    };

    this.printMatchingLines = function () {
        var lines = that.getMatchingLines();
        var hdrText;
        if (_settings.uniqueLines)
            hdrText = "Unique lines with matches";
        else
            hdrText = "Lines with matches";
        log("\n"+hdrText+" ("+lines.length+"):");
        for (var l in lines) {
            log(lines[l]);
        }
    };
}

exports.Searcher = Searcher;<|endoftext|>


<commit_before>module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('bower.json'),
    jshint: {
      grunt: {
        src: ['Gruntfile.js']
      },
      main: {
        src: ['tock.js'],
        options: {
          'browser': true,
          'camelcase': true,
          'curly': true,
          'devel': true,
          'eqeqeq': true,
          'newcap': true,
          'quotmark': 'single',
          'undef': true,
          'unused': true
        }
      },
    },
    uglify: {
      options: {
        banner: '// Tock.js (version <%= pkg.version %>) <%= pkg.homepage %>\n// License: <%= pkg.license %>\n'
      },
      main: {
        src: '<%= jshint.main.src %>',
        dest: 'tock.min.js'
      }
    },
    watch: {
      main: {
        files: '<%= jshint.main.src %>',
        tasks: ['jshint:main', 'uglify:main']
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('default', ['jshint', 'uglify']);
};
<commit_msg>Add grunt-jscs and set some style requirements.<commit_after>module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('bower.json'),
    jshint: {
      grunt: {
        src: ['Gruntfile.js']
      },
      main: {
        src: ['tock.js'],
        options: {
          'browser': true,
          'camelcase': true,
          'curly': true,
          'devel': true,
          'eqeqeq': true,
          'newcap': true,
          'quotmark': 'single',
          'undef': true,
          'unused': true
        }
      },
    },
    jscs: {
      src: '<%= jshint.main.src %>',
      options: {
        'disallowSpacesInNamedFunctionExpression': {
          'beforeOpeningRoundBrace': true
        },
        'disallowSpacesInFunctionExpression': {
          'beforeOpeningRoundBrace': true
        },
        'disallowSpacesInAnonymousFunctionExpression': {
          'beforeOpeningRoundBrace': true
        },
        'disallowSpacesInFunctionDeclaration': {
          'beforeOpeningRoundBrace': true
        },
        'disallowSpaceBeforeBinaryOperators': [
          ','
        ],
        'requireSpaceBeforeBinaryOperators': true,
        'requireSpaceAfterBinaryOperators': true,
        'requireCamelCaseOrUpperCaseIdentifiers': true,
        'requireCapitalizedConstructors': true,
        'disallowMixedSpacesAndTabs': true,
        'disallowTrailingWhitespace': true,
        'disallowTrailingComma': true,
        'disallowSpaceAfterPrefixUnaryOperators': true,
        'disallowSpaceBeforePostfixUnaryOperators': true,
        'disallowSpacesInsideArrayBrackets': true,
        'disallowSpacesInsideParentheses': true,
        'requireSpaceAfterKeywords': [
          'if',
          'else',
          'for',
          'while',
          'do',
          'switch',
          'case',
          'return',
          'try',
          'catch',
          'typeof'
        ],
        'validateIndentation': 2,
        'validateLineBreaks': 'LF',
        'validateQuoteMarks': "'"
      }
    },
    uglify: {
      options: {
        banner: '// Tock.js (version <%= pkg.version %>) <%= pkg.homepage %>\n// License: <%= pkg.license %>\n'
      },
      main: {
        src: '<%= jshint.main.src %>',
        dest: 'tock.min.js'
      }
    },
    watch: {
      main: {
        files: '<%= jshint.main.src %>',
        tasks: ['jshint:main', 'uglify:main']
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-jscs');

  grunt.registerTask('default', ['jshint', 'jscs', 'uglify']);
};
<|endoftext|>