<commit_before>var path = require('path'),
    connect_livereload = require('connect-livereload');
    version = require('./tasks/version');

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-compress');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-open');
    grunt.loadNpmTasks('grunt-text-replace');
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-sync');

    version(grunt);

    var ports = {
        serverDev: 8000,
        serverDist: 8001,
        livereload: 35353
    };

    var dirs = {
        app: 'app',
        build: 'app/.build',
        dist: 'dist',
        lib: 'app/lib',
        releases: 'releases',
        typings: 'app/typings'
    };

    var files = {
    };

    var packageJson;

    function refresh() {
        packageJson = grunt.file.readJSON("package.json");
        grunt.config.set('files.zip', path.join(dirs.releases, packageJson.version + ".zip"));
    }

    function mount(connect, dir) {
        return connect.static(path.resolve(dir));
    }

    refresh();

    grunt.initConfig({

        ports: ports,
        dirs: dirs,

        typescript: {
            build: {
                src: [
                    '<%= dirs.app %>/**/*.ts',
                    '!<%= dirs.lib %>/**/*.ts',
                    '<%= dirs.typings %>/*.ts'
                ],
                dest: dirs.build,
                options: {
                    rootDir: dirs.app,
                    module: 'amd',
                    target: 'es5',
                    sourceMap: true
                }
            }
        },

        clean: {
            build : ['<%= dirs.build %>/*'],
            dist : ['<%= dirs.dist %>/*'],
            // remove every file that isn't needed in production
            minified: {
                files: [
                    {
                        // everything in dist/ except these
                        src: [
                            '<%= dirs.dist %>/*',
                            '!<%= dirs.dist %>/lib',
                            '!<%= dirs.dist %>/Assets',
                            '!<%= dirs.dist %>/favicon.ico',
                            '!<%= dirs.dist %>/img',
                            '!<%= dirs.dist %>/App.js',
                            '!<%= dirs.dist %>/config.json',
                            '!<%= dirs.dist %>/index.html',
                            '!<%= dirs.dist %>/maintenance.html',
                            '!<%= dirs.dist %>/l10n.json',
                            '!<%= dirs.dist %>/styles.css',
                            '!<%= dirs.dist %>/require-config.js'
                        ]
                    },
                    {
                        // every file in dist/lib/ except these
                        src: [
                            '<%= dirs.dist %>/lib/**',
                            '!<%= dirs.dist %>/lib/etch/dist/etch.js',
                            '!<%= dirs.dist %>/lib/exjs/dist/ex.js',
                            '!<%= dirs.dist %>/lib/extensions/dist/extensions.js',
                            '!<%= dirs.dist %>/lib/intersection/intersection.js',
                            '!<%= dirs.dist %>/lib/key-codes/dist/key-codes.js',
                            '!<%= dirs.dist %>/lib/lzma/src/lzma.js',
                            '!<%= dirs.dist %>/lib/lzma/src/lzma_worker.js',
                            '!<%= dirs.dist %>/lib/minerva/dist/minerva.min.js',
                            '!<%= dirs.dist %>/lib/minerva/dist/minerva.min.js.map',
                            '!<%= dirs.dist %>/lib/nullstone/dist/nullstone.min.js',
                            '!<%= dirs.dist %>/lib/nullstone/dist/nullstone.min.js.map',
                            '!<%= dirs.dist %>/lib/pixelpalette/dist/PixelPalette.js',
                            '!<%= dirs.dist %>/lib/RecorderJS/recorder.js',
                            '!<%= dirs.dist %>/lib/tone/**',
                            '!<%= dirs.dist %>/lib/text/text.js',
                            '!<%= dirs.dist %>/lib/tweenjs/src/Tween.js',
                            '!<%= dirs.dist %>/lib/utils/dist/utils.js'
                        ],
                        filter: 'isFile'
                    },
                    {
                        // every folder in dist/lib/ except these
                        src: [
                            '<%= dirs.dist %>/lib/*',
                            '!<%= dirs.dist %>/lib/etch/**',
                            '!<%= dirs.dist %>/lib/exjs/**',
                            '!<%= dirs.dist %>/lib/extensions/**',
                            '!<%= dirs.dist %>/lib/intersection/**',
                            '!<%= dirs.dist %>/lib/key-codes/**',
                            '!<%= dirs.dist %>/lib/lzma/**',
                            '!<%= dirs.dist %>/lib/minerva/**',
                            '!<%= dirs.dist %>/lib/nullstone/**',
                            '!<%= dirs.dist %>/lib/pixelpalette/**',
                            '!<%= dirs.dist %>/lib/RecorderJS/**',
                            '!<%= dirs.dist %>/lib/text/**',
                            '!<%= dirs.dist %>/lib/tone/**',
                            '!<%= dirs.dist %>/lib/tweenjs/**',
                            '!<%= dirs.dist %>/lib/utils/**'
                        ]
                    }
                ]
            }
        },

        replace: {
            minified: {
                src: ['<%= dirs.dist %>/index.html'],
                overwrite: true,
                replacements: [
                    {
                        from: /src=".*require.min.js"/g,
                        to: 'src="App.js"'
                    },
                    {
                        from: '<script src="//localhost:35353/livereload.js"></script>',
                        to: ''
                    }
                ]
            },
            php: {
                src: ['<%= dirs.dist %>/index.html'],
                overwrite: true,
                replacements: [
                    {
                        from: '$title',
                        to: '<?php if(isset($_GET["t"])){ echo \': \' . htmlentities($_GET["t"], ENT_QUOTES, \'UTF-8\');} ?>'
                    }
                ]
            }
        },

        copy: {
            assets: {
                files: [
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>/Assets/',
                        src: ['**'],
                        dest: '<%= dirs.build %>/Assets/'
                    }
                ]
            },
            dist: {
                files: [
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['require-config.js'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>/img',
                        src: ['**'],
                        dest: '<%= dirs.dist %>/img/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>/lib',
                        src: ['**'],
                        dest: '<%= dirs.dist %>/lib/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.build %>',
                        src: ['**'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['config.json'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['l10n.json'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['index.html'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['maintenance.html'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['styles.css'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['favicon.ico'],
                        dest: '<%= dirs.dist %>/'
                    }
                ]
            }
        },

        connect: {
            dev: {
                options: {
                    port: ports.serverDev,
                    base: dirs.app,
                    keepalive: true,
                    middleware: function (connect) {
                        return [
                            //connect_livereload({ port: ports.livereload }),
                            mount(connect, dirs.build),
                            mount(connect, dirs.app)
                        ];
                    }
                }
            },
            livereload: {
                options: {
                    port: ports.serverDev,
                    base: dirs.app,
                    middleware: function (connect) {
                        return [
                            connect_livereload({ port: ports.livereload }),
                            mount(connect, dirs.build),
                            mount(connect, dirs.app)
                        ];
                    }
                }
            },
            dist: {
                options: {
                    port: ports.serverDist,
                    base: dirs.dist,
                    keepalive: true,
                    middleware: function (connect) {
                        return [
                            mount(connect, dirs.dist)
                        ];
                    }
                }
            }
        },

        open: {
            serve: {
                path: 'http://localhost:<%= ports.server %>/index.html'
            }
        },

        // Please leave this in for Luke P. He uses watch:dev instead of serve:dev
        watch: {
            src: {
                files: [
                    '<%= dirs.app %>/**/*.ts',
                    '!<%= dirs.lib %>/**/*.ts',
                    'config.json'
                ],
                tasks: ['typescript:build'],
                options: {
                    livereload: ports.livereload
                }
            }
        },

        exec: {
            minify: {
                cmd: 'node app/lib/r.js/dist/r.js -o app.build.js'
                // uncomment if you want to test an unminified dist build
                //cmd: 'node app/lib/r.js/dist/r.js -o app.build.js optimize=none'
            }//,
            //dist: {
            //    cmd: 'git subtree push --prefix dist origin dist'
            //}
        },

        compress: {
            zip: {
                options: {
                    mode: 'zip',
                    archive: '<%= dirs.dist %>.zip',
                    level: 9
                },
                files: [
                    {
                        expand: true,
                        cwd: '<%= dirs.dist %>/',
                        src: ['**']
                    }
                ]
            }
        },

        version: {
            bump: {
            },
            apply: {
                src: './VersionTemplate.ts',
                dest: './app/_Version.ts'
            }
        },

        sync: {
            bowerComponents: {
                files: [
                    {
                        // all files that need to be copied from /lib to /src/typings post bower install
                        cwd: '<%= dirs.lib %>',
                        expand: true,
                        flatten: true,
                        src: [
                            'etch/dist/etch.d.ts',
                            'extensions/typings/extensions.d.ts',
                            'key-codes/dist/key-codes.d.ts',
                            'minerva/dist/minerva.d.ts',
                            'nullstone/dist/nullstone.d.ts',
                            'tone/utils/TypeScript/Tone.d.ts',
                            'utils/dist/utils.d.ts'
                        ],
                        dest: '<%= dirs.typings %>'
                    }
                ]
            }
        }
    });

    grunt.registerTask('default', ['clean:build', 'typescript:build', 'copy:assets']);
    grunt.registerTask('bump:patch', ['version:bump', 'version:apply']);
    grunt.registerTask('bump:minor', ['version:bump:minor', 'version:apply']);
    grunt.registerTask('bump:major', ['version:bump:major', 'version:apply']);
    grunt.registerTask('serve:dev', ['default', 'connect:dev', 'open']);
    grunt.registerTask('watch:dev', ['default', 'connect:livereload', 'watch']);
    grunt.registerTask('serve:dist', ['connect:dist', 'open']);

    grunt.registerTask('dist', '', function() {

        //grunt.task.run('bump:patch');

        refresh();

        grunt.task.run(
            'clean:dist',
            'copy:dist',
            'exec:minify',
            'clean:minified',
            'replace:minified',
            'replace:php',
            'compress:zip'
        );
    });

};<commit_msg>added serve task<commit_after>var path = require('path'),
    connect_livereload = require('connect-livereload');
    version = require('./tasks/version');

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-compress');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-open');
    grunt.loadNpmTasks('grunt-text-replace');
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-sync');

    version(grunt);

    var ports = {
        serverDev: 8000,
        serverDist: 8001,
        livereload: 35353
    };

    var dirs = {
        app: 'app',
        build: 'app/.build',
        dist: 'dist',
        lib: 'app/lib',
        releases: 'releases',
        typings: 'app/typings'
    };

    var files = {
    };

    var packageJson;

    function refresh() {
        packageJson = grunt.file.readJSON("package.json");
        grunt.config.set('files.zip', path.join(dirs.releases, packageJson.version + ".zip"));
    }

    function mount(connect, dir) {
        return connect.static(path.resolve(dir));
    }

    refresh();

    grunt.initConfig({

        ports: ports,
        dirs: dirs,

        typescript: {
            build: {
                src: [
                    '<%= dirs.app %>/**/*.ts',
                    '!<%= dirs.lib %>/**/*.ts',
                    '<%= dirs.typings %>/*.ts'
                ],
                dest: dirs.build,
                options: {
                    rootDir: dirs.app,
                    module: 'amd',
                    target: 'es5',
                    sourceMap: true
                }
            }
        },

        clean: {
            build : ['<%= dirs.build %>/*'],
            dist : ['<%= dirs.dist %>/*'],
            // remove every file that isn't needed in production
            minified: {
                files: [
                    {
                        // everything in dist/ except these
                        src: [
                            '<%= dirs.dist %>/*',
                            '!<%= dirs.dist %>/lib',
                            '!<%= dirs.dist %>/Assets',
                            '!<%= dirs.dist %>/favicon.ico',
                            '!<%= dirs.dist %>/img',
                            '!<%= dirs.dist %>/App.js',
                            '!<%= dirs.dist %>/config.json',
                            '!<%= dirs.dist %>/index.html',
                            '!<%= dirs.dist %>/maintenance.html',
                            '!<%= dirs.dist %>/l10n.json',
                            '!<%= dirs.dist %>/styles.css',
                            '!<%= dirs.dist %>/require-config.js'
                        ]
                    },
                    {
                        // every file in dist/lib/ except these
                        src: [
                            '<%= dirs.dist %>/lib/**',
                            '!<%= dirs.dist %>/lib/etch/dist/etch.js',
                            '!<%= dirs.dist %>/lib/exjs/dist/ex.js',
                            '!<%= dirs.dist %>/lib/extensions/dist/extensions.js',
                            '!<%= dirs.dist %>/lib/intersection/intersection.js',
                            '!<%= dirs.dist %>/lib/key-codes/dist/key-codes.js',
                            '!<%= dirs.dist %>/lib/lzma/src/lzma.js',
                            '!<%= dirs.dist %>/lib/lzma/src/lzma_worker.js',
                            '!<%= dirs.dist %>/lib/minerva/dist/minerva.min.js',
                            '!<%= dirs.dist %>/lib/minerva/dist/minerva.min.js.map',
                            '!<%= dirs.dist %>/lib/nullstone/dist/nullstone.min.js',
                            '!<%= dirs.dist %>/lib/nullstone/dist/nullstone.min.js.map',
                            '!<%= dirs.dist %>/lib/pixelpalette/dist/PixelPalette.js',
                            '!<%= dirs.dist %>/lib/RecorderJS/recorder.js',
                            '!<%= dirs.dist %>/lib/tone/**',
                            '!<%= dirs.dist %>/lib/text/text.js',
                            '!<%= dirs.dist %>/lib/tweenjs/src/Tween.js',
                            '!<%= dirs.dist %>/lib/utils/dist/utils.js'
                        ],
                        filter: 'isFile'
                    },
                    {
                        // every folder in dist/lib/ except these
                        src: [
                            '<%= dirs.dist %>/lib/*',
                            '!<%= dirs.dist %>/lib/etch/**',
                            '!<%= dirs.dist %>/lib/exjs/**',
                            '!<%= dirs.dist %>/lib/extensions/**',
                            '!<%= dirs.dist %>/lib/intersection/**',
                            '!<%= dirs.dist %>/lib/key-codes/**',
                            '!<%= dirs.dist %>/lib/lzma/**',
                            '!<%= dirs.dist %>/lib/minerva/**',
                            '!<%= dirs.dist %>/lib/nullstone/**',
                            '!<%= dirs.dist %>/lib/pixelpalette/**',
                            '!<%= dirs.dist %>/lib/RecorderJS/**',
                            '!<%= dirs.dist %>/lib/text/**',
                            '!<%= dirs.dist %>/lib/tone/**',
                            '!<%= dirs.dist %>/lib/tweenjs/**',
                            '!<%= dirs.dist %>/lib/utils/**'
                        ]
                    }
                ]
            }
        },

        replace: {
            minified: {
                src: ['<%= dirs.dist %>/index.html'],
                overwrite: true,
                replacements: [
                    {
                        from: /src=".*require.min.js"/g,
                        to: 'src="App.js"'
                    },
                    {
                        from: '<script src="//localhost:35353/livereload.js"></script>',
                        to: ''
                    }
                ]
            },
            php: {
                src: ['<%= dirs.dist %>/index.html'],
                overwrite: true,
                replacements: [
                    {
                        from: '$title',
                        to: '<?php if(isset($_GET["t"])){ echo \': \' . htmlentities($_GET["t"], ENT_QUOTES, \'UTF-8\');} ?>'
                    }
                ]
            }
        },

        copy: {
            assets: {
                files: [
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>/Assets/',
                        src: ['**'],
                        dest: '<%= dirs.build %>/Assets/'
                    }
                ]
            },
            dist: {
                files: [
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['require-config.js'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>/img',
                        src: ['**'],
                        dest: '<%= dirs.dist %>/img/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>/lib',
                        src: ['**'],
                        dest: '<%= dirs.dist %>/lib/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.build %>',
                        src: ['**'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['config.json'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['l10n.json'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['index.html'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['maintenance.html'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['styles.css'],
                        dest: '<%= dirs.dist %>/'
                    },
                    {
                        expand: true,
                        cwd: '<%= dirs.app %>',
                        src: ['favicon.ico'],
                        dest: '<%= dirs.dist %>/'
                    }
                ]
            }
        },

        connect: {
            dev: {
                options: {
                    port: ports.serverDev,
                    base: dirs.app,
                    keepalive: true,
                    middleware: function (connect) {
                        return [
                            //connect_livereload({ port: ports.livereload }),
                            mount(connect, dirs.build),
                            mount(connect, dirs.app)
                        ];
                    }
                }
            },
            livereload: {
                options: {
                    port: ports.serverDev,
                    base: dirs.app,
                    middleware: function (connect) {
                        return [
                            connect_livereload({ port: ports.livereload }),
                            mount(connect, dirs.build),
                            mount(connect, dirs.app)
                        ];
                    }
                }
            },
            dist: {
                options: {
                    port: ports.serverDist,
                    base: dirs.dist,
                    keepalive: true,
                    middleware: function (connect) {
                        return [
                            mount(connect, dirs.dist)
                        ];
                    }
                }
            }
        },

        open: {
            serve: {
                path: 'http://localhost:<%= ports.server %>/index.html'
            }
        },

        // Please leave this in for Luke P. He uses watch:dev instead of serve:dev
        watch: {
            src: {
                files: [
                    '<%= dirs.app %>/**/*.ts',
                    '!<%= dirs.lib %>/**/*.ts',
                    'config.json'
                ],
                tasks: ['typescript:build'],
                options: {
                    livereload: ports.livereload
                }
            }
        },

        exec: {
            minify: {
                cmd: 'node app/lib/r.js/dist/r.js -o app.build.js'
                // uncomment if you want to test an unminified dist build
                //cmd: 'node app/lib/r.js/dist/r.js -o app.build.js optimize=none'
            }//,
            //dist: {
            //    cmd: 'git subtree push --prefix dist origin dist'
            //}
        },

        compress: {
            zip: {
                options: {
                    mode: 'zip',
                    archive: '<%= dirs.dist %>.zip',
                    level: 9
                },
                files: [
                    {
                        expand: true,
                        cwd: '<%= dirs.dist %>/',
                        src: ['**']
                    }
                ]
            }
        },

        version: {
            bump: {
            },
            apply: {
                src: './VersionTemplate.ts',
                dest: './app/_Version.ts'
            }
        },

        sync: {
            bowerComponents: {
                files: [
                    {
                        // all files that need to be copied from /lib to /src/typings post bower install
                        cwd: '<%= dirs.lib %>',
                        expand: true,
                        flatten: true,
                        src: [
                            'etch/dist/etch.d.ts',
                            'extensions/typings/extensions.d.ts',
                            'key-codes/dist/key-codes.d.ts',
                            'minerva/dist/minerva.d.ts',
                            'nullstone/dist/nullstone.d.ts',
                            'tone/utils/TypeScript/Tone.d.ts',
                            'utils/dist/utils.d.ts'
                        ],
                        dest: '<%= dirs.typings %>'
                    }
                ]
            }
        }
    });

    grunt.registerTask('default', ['clean:build', 'typescript:build', 'copy:assets']);
    grunt.registerTask('bump:patch', ['version:bump', 'version:apply']);
    grunt.registerTask('bump:minor', ['version:bump:minor', 'version:apply']);
    grunt.registerTask('bump:major', ['version:bump:major', 'version:apply']);
    grunt.registerTask('serve', ['serve:dev']);
    grunt.registerTask('serve:dev', ['default', 'connect:dev', 'open']);
    grunt.registerTask('watch:dev', ['default', 'connect:livereload', 'watch']);
    grunt.registerTask('serve:dist', ['connect:dist', 'open']);

    grunt.registerTask('dist', '', function() {

        //grunt.task.run('bump:patch');

        refresh();

        grunt.task.run(
            'clean:dist',
            'copy:dist',
            'exec:minify',
            'clean:minified',
            'replace:minified',
            'replace:php',
            'compress:zip'
        );
    });

};<|endoftext|>


<commit_before>module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),
        replace: {
            datatables_images: {
                src: ["bower_components/datatables-plugins/integration/foundation/dataTables.foundation.css"],
                overwrite: true,
                replacements: [{
                    from: "images/",
                    to: "../img/"
                }]
            }
        },
        uglify: {
            modernizr: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/modernizr.min.js": ["bower_components/modernizr/modernizr.js"]
                }
            },
            datatables: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/datatables/datatables.min.js": ["bower_components/datatables/media/js/jquery.datatables.js"]
                }
            },
            datatablesplugins: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/datatables/datatables.foundation.min.js": ["bower_components/datatables-plugins/integration/foundation/datatables.foundation.js"]
                }
            },
            fastclick: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/fastclick.min.js": ["bower_components/fastclick/lib/fastclick.js"]
                }
            }
        },
        bowercopy: {
            js: {
                options: {
                    destPrefix: "src/sgd/frontend/yeastgenome/static/js/build"
                },
                files: {
                    "cytoscape.min.js": "cytoscape/documentation/js/cytoscape.min.js",
                    "arbor.js": "cytoscape/documentation/js/arbor.js",
                    "foundation.min.js": "foundation/js/foundation.min.js",
                    "jquery.min.js": "jquery/dist/jquery.min.js",
                    "kinetic.min.js": "kineticjs/kinetic.min.js",
                    "respond.min.js": "respond/dest/respond.min.js",
                    "rem.min.js": "rem-unit-polyfill/js/rem.min.js",
                    "nouislider.min.js": "nouislider/jquery.nouislider.min.js"
                }
            },
            scss: {
                options: {
                    destPrefix: "src/sgd/frontend/yeastgenome/static/scss"
                },
                files: {
                    "normalize.scss": "foundation/scss/normalize.scss",
                    "build/_nouislider.scss": "nouislider/jquery.nouislider.css",
                    "build/_dataTables.foundation.scss": "datatables-plugins/integration/foundation/dataTables.foundation.css"
                }
            },
            fonts: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/fonts": "font-awesome/fonts"
                }
            },
            images: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/img": "datatables-plugins/integration/foundation/images"
                }
            }

        }
    });

    grunt.loadNpmTasks('grunt-text-replace');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks("grunt-bowercopy");

    grunt.registerTask('default', ['replace','uglify', 'bowercopy']);
};<commit_msg>Cytoscape components have moved.<commit_after>module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),
        replace: {
            datatables_images: {
                src: ["bower_components/datatables-plugins/integration/foundation/dataTables.foundation.css"],
                overwrite: true,
                replacements: [{
                    from: "images/",
                    to: "../img/"
                }]
            }
        },
        uglify: {
            modernizr: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/modernizr.min.js": ["bower_components/modernizr/modernizr.js"]
                }
            },
            datatables: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/datatables/datatables.min.js": ["bower_components/datatables/media/js/jquery.datatables.js"]
                }
            },
            datatablesplugins: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/datatables/datatables.foundation.min.js": ["bower_components/datatables-plugins/integration/foundation/datatables.foundation.js"]
                }
            },
            fastclick: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/js/build/fastclick.min.js": ["bower_components/fastclick/lib/fastclick.js"]
                }
            }
        },
        bowercopy: {
            js: {
                options: {
                    destPrefix: "src/sgd/frontend/yeastgenome/static/js/build"
                },
                files: {
                    "cytoscape.min.js": "cytoscape/dist/cytoscape.min.js",
                    "arbor.js": "cytoscape/lib/arbor.js",
                    "foundation.min.js": "foundation/js/foundation.min.js",
                    "jquery.min.js": "jquery/dist/jquery.min.js",
                    "kinetic.min.js": "kineticjs/kinetic.min.js",
                    "respond.min.js": "respond/dest/respond.min.js",
                    "rem.min.js": "rem-unit-polyfill/js/rem.min.js",
                    "nouislider.min.js": "nouislider/jquery.nouislider.min.js"
                }
            },
            scss: {
                options: {
                    destPrefix: "src/sgd/frontend/yeastgenome/static/scss"
                },
                files: {
                    "normalize.scss": "foundation/scss/normalize.scss",
                    "build/_nouislider.scss": "nouislider/jquery.nouislider.css",
                    "build/_dataTables.foundation.scss": "datatables-plugins/integration/foundation/dataTables.foundation.css"
                }
            },
            fonts: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/fonts": "font-awesome/fonts"
                }
            },
            images: {
                files: {
                    "src/sgd/frontend/yeastgenome/static/img": "datatables-plugins/integration/foundation/images"
                }
            }

        }
    });

    grunt.loadNpmTasks('grunt-text-replace');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks("grunt-bowercopy");

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


<commit_before>'use strict';

/**
 *
 */
exports = module.exports = function(params) {
	var mongoose = params.mongoose;
	var absenceElemSchema = new mongoose.Schema({
        quantity: { type: Number, required: true },         // quantity equal du duration of period in the planning
        consumedQuantity: { type: Number, required: true }, // quantity removed from vacation right according to Right.consuption
                                                            // consuption=proportion: attendance percentage from RightCollection
                                                            // consuption=businessDays: businessDays from RightCollection
        events: [{ type: mongoose.Schema.Types.ObjectId, ref: 'CalendarEvent' }],

        user: {                                     // absence owner
          id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
          name: { type: String, required: true }
        },

        right: {                                    // right parameters used on absence creation
            id: { type: mongoose.Schema.Types.ObjectId, ref: 'Right', required: true },
            name: { type: String, required: true },
            quantity_unit: { type: String, enum:['D', 'H'], required: true },
            type: {
                id: { type: mongoose.Schema.Types.ObjectId, ref: 'Type', required: true },
                name: { type: String, required: true },
                color: { type: String }
            },
            renewal: {
                id: { type: mongoose.Schema.Types.ObjectId, ref: 'RightRenewal' , required: true },
                start: { type: Date, required: true },
                finish: { type: Date, required: true }
            },
            consuption: {                             // consuption type
                type: String,
                enum:['proportion', 'businessDays', 'workingDays'],  // proportion: use the attendance percentage defined in user right collection
                required: true                                       // businessDays: next business days are consumed up to consuptionBusinessDaysLimit
                                                                     // workingDays: full working days are consumed
            },

            consuptionBusinessDaysLimit: { type: Number, default: 5 } // Used if consuption=businessDays
		}
	});

    /**
     * Pre-save hook
     *
     */
    absenceElemSchema.pre('validate', function(next) {

        let err;

        if (!this.events || this.events.length === 0) {
            err = new Error('Invalid event list on absence element');
            return next(err);
        }

        if (this.quantity <= 0) {
            err = new Error('Invalid duration quantity on absence element');
            return next(err);
        }

        if (this.consumedQuantity <= 0) {
            err = new Error('Invalid consuption on absence element');
            return next(err);
        }

        next();
    });

	/**
	 * Find next absence element in same request or null if this element is the last
	 * @return {Promise} Mongoose promise
	 */
	absenceElemSchema.methods.next = function(callback) {
		// TODO
	};

    /**
     * Get an array with one date per leave day, incuding week-ends
     * @return {array}
     */
    absenceElemSchema.methods.getLeaveDays = function() {

        let dtstart = this.events[0].dtstart;
        let dtend = this.events[this.events.length-1].dtend;

        let days = [];

        let loop = new Date(dtstart);
        loop.setHours(0,0,0,0);

        while (loop < dtend) {
            days.push(new Date(loop));
            loop.setDate(loop.getDate() + 1);
        }

        return days;
	};

    /**
     * Get leave days until back to work, incuding week-end
     *
     * @param {Array}  appliquantWorkingDays List of working days of the request appliquant
     *
     * @returns {[[Type]]} [[Description]]
     */
    absenceElemSchema.methods.getLeaveDaysUntilBack = function(appliquantWorkingDays) {

        let days = this.getLeaveDays();

        let dtend = this.events[this.events.length-1].dtend;

        let loop = new Date(dtend);
        loop.setHours(0,0,0,0);

        let d;

        do {
            loop.setDate(loop.getDate()+1);
            d = loop.getDay();

        } while(-1 === appliquantWorkingDays.indexOf(d));

        return days;
    };

    /**
     * Get additional deducted quantity for part-time collections
     * this method get the number of days in base business days witch are not in applicant working days
     * this method is used for consuption method : businessDays or WorkingDays
     *
     * @param {Array}  baseAttendanceDays    List of business days or working days for a 100% attendance
     *
     * @param {Array}  appliquantWorkingDays    List of working days of the request appliquant
     *                                          full days based on the applicant working times calendar
     *
     * @return {Number} Number of days
     */
    absenceElemSchema.methods.getAdditionalDeductedQuantity = function(baseAttendanceDays, appliquantWorkingDays) {

        if ('D' !== this.right.quantity_unit) {
            throw new Error('This methods is not appropriate with this quantity unit');
        }

        let days = this.getLeaveDaysUntilBack(appliquantWorkingDays);
        let d, total = 0;

        for (var i=0; i<days.length; i++) {
            d = days[i].getDay();
            if (-1 !== baseAttendanceDays.indexOf(d)) { // ignore week-end
                if (-1 === appliquantWorkingDays.indexOf(d)) { // ignore days allready in quantity
                    total += 1;
                }
            }
        }

        return total;
    };

    /**
     * Get list of working days using the working times calendar associated to applicant in the absence element period
     * Return an array with dates where the applicant should work but is not, excepted from the last worked date ,
     * this is the date where the applicant is back to work
     * Non working days are ignored in this methods because they are also ignored in the consumption total
     *
     *
     * @return {Promise}
     */
    absenceElemSchema.methods.getWorkingDaysUntilBack = function() {

        let elem = this;
        let accountModel = elem.model('Account');

        // we add one week to the end date to get the back to work day
        let endSearch = new Date(elem.dtend);
        endSearch.setDate(endSearch.getDate()+7);

        return new Promise((resolve, reject) => {

            let find = accountModel.find().where('user.id', elem.user.id);

            find.exec().then((account) => {
                return account.getPeriodScheduleEvents(elem.dtstart, endSearch);
            })
            .then(era => {
                // filter out the dates after the back to work date

                let events = [];

                let i=0, last = false;

                while (!last && undefined !== era.periods[i]) {
                    let period = era.periods[i];
                    if (period.dtstart > elem.dtend) {
                        last = true;
                    }

                    events.push(period);
                    i++;
                }

                resolve(events);
            })
            .catch(reject);
        });
    };

    /**
     * Get the date where the user can be back to work
     * hours are ignored
     *
     * @return {Promise}
     */
    absenceElemSchema.methods.getBackDate = function() {
        return new Promise((resolve, reject) => {
            this.getWorkingDaysUntilBack().then(events => {

                if (events.length === 0) {
                    throw new Error('Invalid back to work date from the getWorkingDaysUntilBack method');
                }

                let backDate = new Date(events[events.length-1].dtstart);
                backDate.setHours(0,0,0,0);
                resolve(backDate);
            })
            .catch(reject);
        });
    };

    /**
     * Get the working days in the absence element period
     * @returns {Promise}   Number
     */
    absenceElemSchema.methods.getWorkingDays = function() {
        return new Promise((resolve, reject) => {
            this.getWorkingDaysUntilBack().then(events => {

                if (events.length === 0) {
                    throw new Error('Invalid back to work date from the getWorkingDaysUntilBack method');
                }

                events.pop();
                resolve(events);
            })
            .catch(reject);
        });
    };

	absenceElemSchema.set('autoIndex', params.autoIndex);

	params.db.model('AbsenceElem', absenceElemSchema);
};
<commit_msg>getWorkingDaysUntilBack<commit_after>'use strict';

/**
 *
 */
exports = module.exports = function(params) {
	var mongoose = params.mongoose;
	var absenceElemSchema = new mongoose.Schema({
        quantity: { type: Number, required: true },         // quantity equal du duration of period in the planning
        consumedQuantity: { type: Number, required: true }, // quantity removed from vacation right according to Right.consuption
                                                            // consuption=proportion: attendance percentage from RightCollection
                                                            // consuption=businessDays: businessDays from RightCollection
        events: [{ type: mongoose.Schema.Types.ObjectId, ref: 'CalendarEvent' }],

        user: {                                     // absence owner
          id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
          name: { type: String, required: true }
        },

        right: {                                    // right parameters used on absence creation
            id: { type: mongoose.Schema.Types.ObjectId, ref: 'Right', required: true },
            name: { type: String, required: true },
            quantity_unit: { type: String, enum:['D', 'H'], required: true },
            type: {
                id: { type: mongoose.Schema.Types.ObjectId, ref: 'Type', required: true },
                name: { type: String, required: true },
                color: { type: String }
            },
            renewal: {
                id: { type: mongoose.Schema.Types.ObjectId, ref: 'RightRenewal' , required: true },
                start: { type: Date, required: true },
                finish: { type: Date, required: true }
            },
            consuption: {                             // consuption type
                type: String,
                enum:['proportion', 'businessDays', 'workingDays'],  // proportion: use the attendance percentage defined in user right collection
                required: true                                       // businessDays: next business days are consumed up to consuptionBusinessDaysLimit
                                                                     // workingDays: full working days are consumed
            },

            consuptionBusinessDaysLimit: { type: Number, default: 5 } // Used if consuption=businessDays
		}
	});

    /**
     * Pre-save hook
     *
     */
    absenceElemSchema.pre('validate', function(next) {

        let err;

        if (!this.events || this.events.length === 0) {
            err = new Error('Invalid event list on absence element');
            return next(err);
        }

        if (this.quantity <= 0) {
            err = new Error('Invalid duration quantity on absence element');
            return next(err);
        }

        if (this.consumedQuantity <= 0) {
            err = new Error('Invalid consuption on absence element');
            return next(err);
        }

        next();
    });

	/**
	 * Find next absence element in same request or null if this element is the last
	 * @return {Promise} Mongoose promise
	 */
	absenceElemSchema.methods.next = function(callback) {
		// TODO
	};

    /**
     * Get an array with one date per leave day, incuding week-ends
     * @return {array}
     */
    absenceElemSchema.methods.getLeaveDays = function() {

        let dtstart = this.events[0].dtstart;
        let dtend = this.events[this.events.length-1].dtend;

        let days = [];

        let loop = new Date(dtstart);
        loop.setHours(0,0,0,0);

        while (loop < dtend) {
            days.push(new Date(loop));
            loop.setDate(loop.getDate() + 1);
        }

        return days;
	};

    /**
     * Get leave days until back to work, incuding week-end
     *
     * @param {Array}  appliquantWorkingDays List of working days of the request appliquant
     *
     * @returns {[[Type]]} [[Description]]
     */
    absenceElemSchema.methods.getLeaveDaysUntilBack = function(appliquantWorkingDays) {

        let days = this.getLeaveDays();

        let dtend = this.events[this.events.length-1].dtend;

        let loop = new Date(dtend);
        loop.setHours(0,0,0,0);

        let d;

        do {
            loop.setDate(loop.getDate()+1);
            d = loop.getDay();

        } while(-1 === appliquantWorkingDays.indexOf(d));

        return days;
    };

    /**
     * Get additional deducted quantity for part-time collections
     * this method get the number of days in base business days witch are not in applicant working days
     * this method is used for consuption method : businessDays or WorkingDays
     *
     * @param {Array}  baseAttendanceDays    List of business days or working days for a 100% attendance
     *
     * @param {Array}  appliquantWorkingDays    List of working days of the request appliquant
     *                                          full days based on the applicant working times calendar
     *
     * @return {Number} Number of days
     */
    absenceElemSchema.methods.getAdditionalDeductedQuantity = function(baseAttendanceDays, appliquantWorkingDays) {

        if ('D' !== this.right.quantity_unit) {
            throw new Error('This methods is not appropriate with this quantity unit');
        }

        let days = this.getLeaveDaysUntilBack(appliquantWorkingDays);
        let d, total = 0;

        for (var i=0; i<days.length; i++) {
            d = days[i].getDay();
            if (-1 !== baseAttendanceDays.indexOf(d)) { // ignore week-end
                if (-1 === appliquantWorkingDays.indexOf(d)) { // ignore days allready in quantity
                    total += 1;
                }
            }
        }

        return total;
    };

    /**
     * Get list of working days using the working times calendar associated to applicant in the absence element period
     * Return an array with dates where the applicant should work but is not, excepted from the last worked date ,
     * this is the date where the applicant is back to work
     * Non working days are ignored in this methods because they are also ignored in the consumption total
     *
     *
     * @return {Promise}
     */
    absenceElemSchema.methods.getWorkingDaysUntilBack = function() {

        let elem = this;
        let accountModel = elem.model('Account');

        // we add one week to the end date to get the back to work day
        let endSearch = new Date(elem.dtend);
        endSearch.setDate(endSearch.getDate()+7);

        return new Promise((resolve, reject) => {

            let find = accountModel.find().where('user.id', elem.user.id);

            find.exec().then((accounts) => {

                if (0 === accounts.length) {
                    throw new Error('No account found for user '+elem.user.id);
                }

                return accounts[0].getPeriodScheduleEvents(elem.dtstart, endSearch);
            })
            .then(era => {
                // filter out the dates after the back to work date

                let events = [];

                let i=0, last = false;

                while (!last && undefined !== era.periods[i]) {
                    let period = era.periods[i];
                    if (period.dtstart > elem.dtend) {
                        last = true;
                    }

                    events.push(period);
                    i++;
                }

                resolve(events);
            })
            .catch(reject);
        });
    };

    /**
     * Get the date where the user can be back to work
     * hours are ignored
     *
     * @return {Promise}
     */
    absenceElemSchema.methods.getBackDate = function() {
        return new Promise((resolve, reject) => {
            this.getWorkingDaysUntilBack().then(events => {

                if (events.length === 0) {
                    throw new Error('Invalid back to work date from the getWorkingDaysUntilBack method');
                }

                let backDate = new Date(events[events.length-1].dtstart);
                backDate.setHours(0,0,0,0);
                resolve(backDate);
            })
            .catch(reject);
        });
    };

    /**
     * Get the working days in the absence element period
     * @returns {Promise}   Number
     */
    absenceElemSchema.methods.getWorkingDays = function() {
        return new Promise((resolve, reject) => {
            this.getWorkingDaysUntilBack().then(events => {

                if (events.length === 0) {
                    throw new Error('Invalid back to work date from the getWorkingDaysUntilBack method');
                }

                events.pop();
                resolve(events);
            })
            .catch(reject);
        });
    };

	absenceElemSchema.set('autoIndex', params.autoIndex);

	params.db.model('AbsenceElem', absenceElemSchema);
};
<|endoftext|>


<commit_before>import {describe, it} from 'mocha';
import {rollup} from 'rollup';
import {expect} from 'chai';
import ejs from '../src/index.js';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

process.chdir(dirname(fileURLToPath(import.meta.url)));

describe('rollup-plugin-ejs', () => {
  function createBundle(sampleFileName = 'main', pluginSettings) {
    return rollup({
      input: `samples/${sampleFileName}.js`,
      plugins: [ejs(pluginSettings)],
    })
  }

  async function getExportedFrom(bundle) {
    const {output: [{code}]} = await bundle.generate({format: 'cjs', exports: 'default'});
    const module = {exports: {}};
    const fn = new Function('module', code);

    fn(module);

    return module.exports;
  }

  function removeSpacesFrom(str) {
    return str.replace(/\s/g, '');
  }

  describe('common', () => {
    it('should convert ejs to tpl function', async () => {
      const bundle = await createBundle();
      const tplFn = await getExportedFrom(bundle);

      expect(tplFn).to.be.a('function');
    });

    it('should convert ejs to tpl function retuning parsed html string', async () => {
      const bundle = await createBundle();
      const tplFn = await getExportedFrom(bundle);

      expect(tplFn({test: 'test'})).to.be.equal('
test
');
    });

    it('should support any file extension with proper ejs content', async () => {
      const bundle = await createBundle('html', {include: ['**/*.html']});
      const tplFn = await getExportedFrom(bundle);

      expect(tplFn({test: 'test'})).to.be.equal('
test
');
    });

    it('should replace link[rel="stylesheet"] without href to empty string', async () => {
      const bundle = await createBundle('cssLoading/oneLinkTag', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn({test: 'test_value'})))
        .to.be.equal('
test_value
');
    });

    it('should replace multiple link[rel="stylesheet"] without href to empty string', async () => {
      const bundle = await createBundle('cssLoading/multipleLinkTags', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn({test: 'test_value'})))
        .to.be.equal('
test_value
');
    });
  });

  describe('data rendering', () => {
    it('should render ejs with provided data', async () => {
      const bundle = await createBundle('renderData', {render: {data: {test: 'test'}}});
      const compiledTpl = await getExportedFrom(bundle);

      expect(compiledTpl.trim())
        .to.be.equal(`<div> <p>test</p> </div>`);
    });

    it('should render ejs with provided data and minify resulted html', async () => {
      const bundle = await createBundle('renderData', {
        render: {
          data: {test: 'test'},
          minifierOptions: {collapseWhitespace: true},
        }
      });
      const compiledTpl = await getExportedFrom(bundle);

      expect(compiledTpl)
        .to.be.equal(`<div><p>test</p></div>`);
    });
  });

  describe('css styles loading', () => {
    it('should load css rules from linked css file to style tag', async () => {
      const bundle = await createBundle('cssLoading/loadCssRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal('');
    });

    it('should load css rules from multiple linked css files to style tags', async () => {
      const bundle = await createBundle('cssLoading/loadMultipleCssRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal(removeSpacesFrom(`

        `));
    });
  });

  describe('sass styles loading', () => {
    it('should load and compile css rules from linked scss file to style tag', async () => {
      const bundle = await createBundle('sassLoading/loadSassRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal('');
    });

    it('should load and compile css rules from multiple linked scss files to style tags', async () => {
      const bundle = await createBundle('sassLoading/loadMultipleSassRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal(removeSpacesFrom(`

        `));
    });

    it('should correctly resolve and load sass imports for multiple nesting levels', async () => {
      const bundle = await createBundle('sassLoading/nested/nested1/loadSassRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal('');
    });

    it('should load and compile mixed css rules from multiple linked css and scss files to style tags', async () => {
      const bundle = await createBundle('sassLoading/loadMultipleMixedRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal(removeSpacesFrom(`

        `));
    });
  });
});

<commit_msg>minor<commit_after>import {describe, it} from 'mocha';
import {rollup} from 'rollup';
import {expect} from 'chai';
import ejs from '../src/index.js';
import {fileURLToPath} from 'url';
import {dirname} from 'path';

process.chdir(dirname(fileURLToPath(import.meta.url)));

describe('rollup-plugin-ejs', () => {
  function createBundle(sampleFileName = 'main', pluginSettings) {
    return rollup({
      input: `samples/${sampleFileName}.js`,
      plugins: [ejs(pluginSettings)],
    })
  }

  async function getExportedFrom(bundle) {
    const {output: [{code}]} = await bundle.generate({format: 'cjs', exports: 'default'});
    const module = {exports: {}};
    const fn = new Function('module', code);

    fn(module);

    return module.exports;
  }

  function removeSpacesFrom(str) {
    return str.replace(/\s/g, '');
  }

  describe('common', () => {
    it('should convert ejs to tpl function', async () => {
      const bundle = await createBundle();
      const tplFn = await getExportedFrom(bundle);

      expect(tplFn).to.be.a('function');
    });

    it('should convert ejs to tpl function retuning parsed html string', async () => {
      const bundle = await createBundle();
      const tplFn = await getExportedFrom(bundle);

      expect(tplFn({test: 'test'})).to.be.equal('
test
');
    });

    it('should support any file extension with proper ejs content', async () => {
      const bundle = await createBundle('html', {include: ['**/*.html']});
      const tplFn = await getExportedFrom(bundle);

      expect(tplFn({test: 'test'})).to.be.equal('
test
');
    });

    it('should replace link[rel="stylesheet"] without href to empty string', async () => {
      const bundle = await createBundle('cssLoading/oneLinkTag', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn({test: 'test_value'})))
        .to.be.equal('
test_value
');
    });

    it('should replace multiple link[rel="stylesheet"] without href to empty string', async () => {
      const bundle = await createBundle('cssLoading/multipleLinkTags', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn({test: 'test_value'})))
        .to.be.equal('
test_value
');
    });
  });

  describe('data rendering', () => {
    it('should render ejs with provided data', async () => {
      const bundle = await createBundle('renderData', {render: {data: {test: 'test'}}});
      const compiledTpl = await getExportedFrom(bundle);

      expect(compiledTpl.trim())
        .to.be.equal(`<div> <p>test</p> </div>`);
    });

    it('should render ejs with provided data and minify resulted html', async () => {
      const bundle = await createBundle('renderData', {
        render: {
          data: {test: 'test'},
          minifierOptions: {collapseWhitespace: true},
        }
      });
      const compiledTpl = await getExportedFrom(bundle);

      expect(compiledTpl)
        .to.be.equal(`<div><p>test</p></div>`);
    });
  });

  describe('css styles loading', () => {
    it('should load css rules from linked css file to style tag', async () => {
      const bundle = await createBundle('cssLoading/loadCssRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal('');
    });

    it('should load css rules from multiple linked css files to style tags', async () => {
      const bundle = await createBundle('cssLoading/loadMultipleCssRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal(removeSpacesFrom(`

        `));
    });
  });

  describe('sass styles loading', () => {
    it('should load and compile css rules from linked scss file to style tag', async () => {
      const bundle = await createBundle('sassLoading/loadSassRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal('');
    });

    it('should load and compile css rules from multiple linked scss files to style tags', async () => {
      const bundle = await createBundle('sassLoading/loadMultipleSassRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal(removeSpacesFrom(`

        `));
    });

    it('should correctly resolve and load sass imports for multiple nesting levels', async () => {
      const bundle = await createBundle('sassLoading/nested/nested1/loadSassRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal('');
    });

    it('should load and compile mixed css rules from multiple linked css and scss files to style tags', async () => {
      const bundle = await createBundle('sassLoading/loadMultipleMixedRules', {loadStyles: true});
      const tplFn = await getExportedFrom(bundle);

      expect(removeSpacesFrom(tplFn()))
        .to.be.equal(removeSpacesFrom(`

        `));
    });
  });
});

<|endoftext|>


<commit_before>import {Defer} from "tooleks";

/**
 * Class BrowserReCaptchaService.
 */
export default class BrowserReCaptchaService {
    /**
     * BrowserReCaptchaService constructor.
     *
     * @param {*} element
     * @param {string} siteKey
     * @param {Function} onVerified
     */
    constructor(element, siteKey, onVerified) {
        this.deffered = new Defer;
        this.element = element;
        this.siteKey = siteKey;
        this.onVerified = onVerified;
        this._getReCaptcha = this._getReCaptcha.bind(this);
        this.execute = this.execute.bind(this);
        this.render = this.render.bind(this);
        this.reset = this.reset.bind(this);
        this.load = this.load.bind(this);
    }

    /**
     * @return {*}
     * @private
     */
    _getReCaptcha() {
        return window["grecaptcha"];
    }

    /**
     * @return {Promise}
     */
    async execute() {
        await this.deffered.promisify();
        this._getReCaptcha().execute(this.widgetId);
    }

    /**
     * @return {Promise}
     */
    async render() {
        await this.deffered.promisify();
        this.widgetId = this._getReCaptcha().render(this.element, {
            sitekey: this.siteKey,
            size: "invisible",
            callback: (response) => {
                this.onVerified.call(this.onVerified, response);
                this.reset();
            },
        });
    }

    /**
     * @return {Promise}
     */
    async reset() {
        await this.deffered.promisify();
        this._getReCaptcha().reset(this.widgetId);
    }

    /**
     * @return {Promise}
     */
    load() {
        // Resolve deferred when reCaptcha will be loaded.
        const timerId = setInterval(() => {
            if (typeof this._getReCaptcha() !== "undefined") {
                this.deffered.resolve();
                clearInterval(timerId);
            }
        });
        return this.deffered.promisify();
    }
}
<commit_msg>Refactored recaptcha service<commit_after>import {Defer} from "tooleks";

/**
 * Class BrowserReCaptchaService.
 */
export default class BrowserReCaptchaService {
    /**
     * BrowserReCaptchaService constructor.
     *
     * @param {*} element
     * @param {string} siteKey
     * @param {Function} onVerified
     */
    constructor(element, siteKey, onVerified) {
        this.deffered = new Defer;
        this.element = element;
        this.siteKey = siteKey;
        this.onVerified = onVerified;
        this._getReCaptcha = this._getReCaptcha.bind(this);
        this.execute = this.execute.bind(this);
        this.render = this.render.bind(this);
        this.reset = this.reset.bind(this);
        this.load = this.load.bind(this);
    }

    /**
     * @return {*}
     * @private
     */
    _getReCaptcha() {
        return window["grecaptcha"];
    }

    /**
     * @return {Promise}
     */
    async execute() {
        const reCaptcha = await this.deffered.promisify();
        reCaptcha.execute(this.widgetId);
    }

    /**
     * @return {Promise}
     */
    async render() {
        const reCaptcha = await this.deffered.promisify();
        this.widgetId = reCaptcha.render(this.element, {
            sitekey: this.siteKey,
            size: "invisible",
            callback: (response) => {
                this.onVerified.call(this.onVerified, response);
                this.reset();
            },
        });
    }

    /**
     * @return {Promise}
     */
    async reset() {
        const reCaptcha = await this.deffered.promisify();
        reCaptcha.reset(this.widgetId);
    }

    /**
     * @return {Promise}
     */
    load() {
        // Resolve deferred when reCaptcha will be loaded.
        const timerId = setInterval(() => {
            const reCaptcha = this._getReCaptcha();
            if (typeof reCaptcha !== "undefined") {
                this.deffered.resolve(reCaptcha);
                clearInterval(timerId);
            }
        });
        return this.deffered.promisify();
    }
}
<|endoftext|>


<commit_before>var auth = angular.module('beer-tab.auth', []);

auth.controller('AuthCtrl', function ($scope, $rootScope, $window, $location, AuthService) {

  $scope.user = {};
  $scope.logIn = function () {
    $window.username = $scope.user.username;
    AuthService.login($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  $scope.signUp = function () {
    AuthService.signup($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  $scope.signout = function () {
    AuthService.signout();
  };

  $scope.redirect = function(path) {
    console.log(path);
    $location.path('/' + path);
  };

});
<commit_msg>add checkRequired directive for over 21<commit_after>var auth = angular.module('beer-tab.auth', []);

auth.controller('AuthCtrl', function ($scope, $rootScope, $window, $location, AuthService) {

  $scope.user = {};
  $scope.logIn = function () {
    $window.username = $scope.user.username;
    AuthService.login($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  $scope.signUp = function () {
    AuthService.signup($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  $scope.signout = function () {
    AuthService.signout();
  };

  $scope.redirect = function (path) {
    console.log(path);
    $location.path('/' + path);
  };

})
.directive('checkRequired', function () {
  return {
    require: 'ngModel',
    restrict: 'A',
    link: function (scope, element, attrs, ngModel) {
      ngModel.$validators.checkRequired = function (modelValue, viewValue) {
        var value = modelValue || viewValue;
        var match = scope.$eval(attrs.ngTrueValue) || true;
        return value && match === value;
      };
    }
  };
});
<|endoftext|>


<commit_before>import * as PropTypes from 'prop-types'
import {Activator} from 'widget/activation/activator'
import {Button} from 'widget/button'
import {Input} from 'widget/input'
import {Panel} from 'widget/panel/panel'
import {ScrollableList} from 'widget/list'
import {Widget} from 'widget/widget'
import {activatable} from 'widget/activation/activatable'
import {compose} from 'compose'
import Label from 'widget/label'
import React, {Component} from 'react'
import _ from 'lodash'
import guid from 'guid'
import moment from 'moment'
import styles from './datePicker.module.css'

const DATE_FORMAT = 'YYYY-MM-DD'

export const momentDate = date =>
    _.isString(date)
        ? moment(date, DATE_FORMAT, true)
        : moment(date)

const pickDate = (date1, date2, func) => {
    date1 = momentDate(date1)
    date2 = momentDate(date2)
    if (!date1.isValid())
        return date2
    else if (!date2.isValid())
        return date1
    else
        return func(date1, date2)
}

export const maxDate = (date1, date2) => pickDate(date1, date2, moment.max)
export const minDate = (date1, date2) => pickDate(date1, date2, moment.min)
export const constrainDate = (date, min, max) => maxDate(minDate(date, max), min)

export class FormDatePicker extends React.Component {
    id = `DatePicker-${guid()}`
    inputElement = React.createRef()

    render() {
        const {input, startDate, endDate, label, autoFocus, tooltip, tooltipPlacement} = this.props
        const date = moment(input.value, DATE_FORMAT)
        return (
            <Activator id={this.id}>
                {panel =>
                    <Widget
                        label={label}
                        tooltip={tooltip}
                        tooltipPlacement={tooltipPlacement}>
                        <DatePickerPanel
                            id={this.id}
                            title={label}
                            date={date.isValid() ? date : moment(startDate, DATE_FORMAT)}
                            startDate={momentDate(startDate)}
                            endDate={momentDate(endDate)}
                            onSelect={date => {
                                const dateString = date.format(DATE_FORMAT)
                                this.inputElement.current.value = dateString
                                input.set(dateString)
                            }}/>

                            <Input
                                ref={this.inputElement}
                                value={input.value}
                                type='text'
                                maxLength={10}
                                autoFocus={autoFocus}
                                className={styles.input}
                                onChange={e => this.setInput(e.target.value)}
                                onBlur={() => this.inputElement.current.value = input.value}
                            />
                            <Button
                                additionalClassName={styles.panelTrigger}
                                chromeless
                                shape='none'
                                icon='calendar-alt'
                                size='small'
                                onClick={() => panel.activate()}
                            />

                    </Widget>
                }
            </Activator>
        )
    }

    setInput(value) {
        const {input, startDate, endDate} = this.props
        const date = momentDate(value)
        // if (date.isValid()) {
        const formattedDate = date.isValid()
            ? constrainDate(date, startDate, endDate).format(DATE_FORMAT)
            : momentDate(startDate).format(DATE_FORMAT)
        input.set(formattedDate)
        // }
    }
}

FormDatePicker.propTypes = {
    endDate: PropTypes.any.isRequired,
    input: PropTypes.object.isRequired,
    startDate: PropTypes.any.isRequired,
    autoFocus: PropTypes.any,
    errorMessage: PropTypes.any,
    label: PropTypes.any,
    tooltip: PropTypes.any,
    tooltipPlacement: PropTypes.any
}

class _DatePickerPanel extends React.Component {
    state = {}

    render() {
        const {title} = this.props
        const {date} = this.state
        if (!date)
            return null
        return (
            <Panel
                className={styles.panel}
                type='modal'>
                <Panel.Header
                    icon='calendar-alt'
                    title={title}/>
                <Panel.Content noVerticalPadding>

                            {this.renderYears()}
                            {this.renderMonths()}
                            {this.renderDays()}

                </Panel.Content>
                <Panel.Buttons onEnter={() => this.select()} onEscape={() => this.close()}>
                    <Panel.Buttons.Main>
                        {this.renderButtons()}
                    </Panel.Buttons.Main>
                </Panel.Buttons>
            </Panel>
        )
    }

    renderYears() {
        const {startDate, endDate} = this.props
        const {date} = this.state
        const startYear = startDate.year()
        const endYear = endDate.year()
        const selectedYear = date.year()
        const options = _.concat(
            _.range(startYear - 5, startYear).map(year => ({label: year})),
            _.range(startYear, endYear + 1).map(year => ({label: year, value: year})),
            _.range(endYear + 1, endYear + 6).map(year => ({label: year}))
        )
        const selectedOption = _.find(options, ({value}) => value === selectedYear)
        return (

                <ScrollableList
                    options={options}
                    selectedOption={selectedOption}
                    onSelect={option => this.updateDate('year', option.value)}
                    alignment='center'
                    keyboard={false}
                    autoCenter
                />

        )
    }

    renderMonths() {
        const {date} = this.state
        const {startDate, endDate} = this.props
        const months = moment.monthsShort()
        const selectedMonth = months[date.month()]
        const firstMonthIndex = date.year() === startDate.year() ? startDate.month() : 0
        const lastMonthIndex = date.year() === endDate.year() ? endDate.month() : 11
        return (

                {months.map((month, i) =>
                    this.renderMonthButton({
                        month,
                        selected: month === selectedMonth,
                        disabled: i < firstMonthIndex || i > lastMonthIndex
                    })
                )}

        )
    }

    renderMonthButton({month, selected, disabled}) {
        return (
            <CalendarButton
                key={month}
                label={month}
                selected={selected}
                disabled={disabled}
                onClick={() => this.updateDate('month', month)}/>
        )
    }

    renderDays() {
        return (

                {this.renderWeekDaysLabels()}
                {this.renderDaysOfMonth()}

        )
    }

    renderWeekDaysLabels() {
        return moment.weekdaysShort().map(weekday =>

        )
    }

    renderDaysOfMonth() {
        const {date} = this.state
        const {startDate, endDate} = this.props
        const firstOfMonth = moment(date).startOf('month')
        const firstToRender = moment(firstOfMonth).startOf('week')
        const lastOfMonth = moment(date).endOf('month')
        const firstDay = date.isSame(startDate, 'month') ? startDate.date() : 1
        const lastDate = date.isSame(endDate, 'month') ? endDate : lastOfMonth
        const lastDay = lastDate.date()
        const lastToRender = moment(lastOfMonth).endOf('week')
        const indexOffset = firstOfMonth.day() - 1
        const daysToRender = lastToRender.diff(firstToRender, 'days') + 1
        const firstIndex = firstDay + indexOffset
        const lastIndex = lastDay + indexOffset
        return _.times(daysToRender,
            i => this.renderMonthDay({
                date,
                i,
                disabled: i < firstIndex || i > lastIndex,
                firstToRender
            })
        )
    }

    renderMonthDay({date, i, firstToRender, disabled}) {
        const buttonDate = moment(firstToRender).add(i, 'day')
        const dayOfMonth = buttonDate.format('DD')
        return (
            <CalendarButton
                key={i}
                label={dayOfMonth}
                selected={buttonDate.isSame(date, 'day')}
                disabled={disabled}
                onClick={() => this.updateDate('date', dayOfMonth)}/>
        )
    }

    renderButtons() {
        return this.isDirty()
            ? <React.Fragment>
                <Panel.Buttons.Cancel onClick={() => this.close()}/>
                <Panel.Buttons.Select onClick={() => this.select()}/>
            </React.Fragment>
            : <Panel.Buttons.Close onClick={() => this.close()}/>
    }

    componentDidMount() {
        const {date} = this.props
        this.setState({
            date: this.toValidRange(date)
        })
    }

    updateDate(unit, value) {
        this.setState(prevState => ({
            date: this.toValidRange(prevState.date.set(unit, value))
        }))

    }

    toValidRange(date) {
        const {startDate, endDate} = this.props
        return constrainDate(date, startDate, endDate)
    }

    select() {
        const {onSelect} = this.props
        this.isDirty() && onSelect && onSelect(this.state.date)
        this.close()
    }

    close() {
        const {activatable} = this.props
        activatable.deactivate()
    }

    isDirty() {
        return !this.props.date.isSame(this.state.date, 'day')
    }
}

const DatePickerPanel = compose(
    _DatePickerPanel,
    activatable({
        id: ({id}) => id,
        policy: () => ({_: 'allow'}),
        alwaysAllow: true
    })
)

class CalendarButton extends Component {
    render() {
        const {label, selected, disabled, onClick} = this.props
        return (
            <Button
                chromeless={!selected}
                look={selected ? 'highlight' : 'transparent'}
                label={label}
                disabled={disabled}
                onClick={onClick}
            />
        )
    }
}

CalendarButton.propTypes = {
    className: PropTypes.any,
    label: PropTypes.any,
    selected: PropTypes.any,
    onClick: PropTypes.any
}
<commit_msg>Implemented temporary fix for DatePicker input.<commit_after>import * as PropTypes from 'prop-types'
import {Activator} from 'widget/activation/activator'
import {Button} from 'widget/button'
import {Input} from 'widget/input'
import {Panel} from 'widget/panel/panel'
import {ScrollableList} from 'widget/list'
import {Widget} from 'widget/widget'
import {activatable} from 'widget/activation/activatable'
import {compose} from 'compose'
import Label from 'widget/label'
import React, {Component} from 'react'
import _ from 'lodash'
import guid from 'guid'
import moment from 'moment'
import styles from './datePicker.module.css'

const DATE_FORMAT = 'YYYY-MM-DD'

export const momentDate = date =>
    _.isString(date)
        ? moment(date, DATE_FORMAT, true)
        : moment(date)

const pickDate = (date1, date2, func) => {
    date1 = momentDate(date1)
    date2 = momentDate(date2)
    if (!date1.isValid())
        return date2
    else if (!date2.isValid())
        return date1
    else
        return func(date1, date2)
}

export const maxDate = (date1, date2) => pickDate(date1, date2, moment.max)
export const minDate = (date1, date2) => pickDate(date1, date2, moment.min)
export const constrainDate = (date, min, max) => maxDate(minDate(date, max), min)

export class FormDatePicker extends React.Component {
    id = `DatePicker-${guid()}`
    inputElement = React.createRef()

    render() {
        const {input, startDate, endDate, label, autoFocus, tooltip, tooltipPlacement} = this.props
        const date = moment(input.value, DATE_FORMAT)
        return (
            <Activator id={this.id}>
                {panel =>
                    <Widget
                        label={label}
                        tooltip={tooltip}
                        tooltipPlacement={tooltipPlacement}>
                        <DatePickerPanel
                            id={this.id}
                            title={label}
                            date={date.isValid() ? date : moment(startDate, DATE_FORMAT)}
                            startDate={momentDate(startDate)}
                            endDate={momentDate(endDate)}
                            onSelect={date => {
                                const dateString = date.format(DATE_FORMAT)
                                this.inputElement.current.value = dateString
                                input.set(dateString)
                            }}/>

                            <Input
                                ref={this.inputElement}
                                value={input.value}
                                type='text'
                                maxLength={10}
                                autoFocus={autoFocus}
                                className={styles.input}
                                onChange={e => this.setInput(e.target.value)}
                                onBlur={() => this.inputElement.current.value = input.value}
                            />
                            <Button
                                additionalClassName={styles.panelTrigger}
                                chromeless
                                shape='none'
                                icon='calendar-alt'
                                size='small'
                                onClick={() => panel.activate()}
                            />

                    </Widget>
                }
            </Activator>
        )
    }

    setInput(value) {
        const {input, startDate, endDate} = this.props
        const date = momentDate(value)
        // const formattedDate = date.isValid()
        //     ? constrainDate(date, startDate, endDate).format(DATE_FORMAT)
        //     : momentDate(startDate).format(DATE_FORMAT)
        // input.set(formattedDate)
        if (date.isValid()) {
            const formattedDate = constrainDate(date, startDate, endDate).format(DATE_FORMAT)
            input.set(formattedDate)
        }
    }
}

FormDatePicker.propTypes = {
    endDate: PropTypes.any.isRequired,
    input: PropTypes.object.isRequired,
    startDate: PropTypes.any.isRequired,
    autoFocus: PropTypes.any,
    errorMessage: PropTypes.any,
    label: PropTypes.any,
    tooltip: PropTypes.any,
    tooltipPlacement: PropTypes.any
}

class _DatePickerPanel extends React.Component {
    state = {}

    render() {
        const {title} = this.props
        const {date} = this.state
        if (!date)
            return null
        return (
            <Panel
                className={styles.panel}
                type='modal'>
                <Panel.Header
                    icon='calendar-alt'
                    title={title}/>
                <Panel.Content noVerticalPadding>

                            {this.renderYears()}
                            {this.renderMonths()}
                            {this.renderDays()}

                </Panel.Content>
                <Panel.Buttons onEnter={() => this.select()} onEscape={() => this.close()}>
                    <Panel.Buttons.Main>
                        {this.renderButtons()}
                    </Panel.Buttons.Main>
                </Panel.Buttons>
            </Panel>
        )
    }

    renderYears() {
        const {startDate, endDate} = this.props
        const {date} = this.state
        const startYear = startDate.year()
        const endYear = endDate.year()
        const selectedYear = date.year()
        const options = _.concat(
            _.range(startYear - 5, startYear).map(year => ({label: year})),
            _.range(startYear, endYear + 1).map(year => ({label: year, value: year})),
            _.range(endYear + 1, endYear + 6).map(year => ({label: year}))
        )
        const selectedOption = _.find(options, ({value}) => value === selectedYear)
        return (

                <ScrollableList
                    options={options}
                    selectedOption={selectedOption}
                    onSelect={option => this.updateDate('year', option.value)}
                    alignment='center'
                    keyboard={false}
                    autoCenter
                />

        )
    }

    renderMonths() {
        const {date} = this.state
        const {startDate, endDate} = this.props
        const months = moment.monthsShort()
        const selectedMonth = months[date.month()]
        const firstMonthIndex = date.year() === startDate.year() ? startDate.month() : 0
        const lastMonthIndex = date.year() === endDate.year() ? endDate.month() : 11
        return (

                {months.map((month, i) =>
                    this.renderMonthButton({
                        month,
                        selected: month === selectedMonth,
                        disabled: i < firstMonthIndex || i > lastMonthIndex
                    })
                )}

        )
    }

    renderMonthButton({month, selected, disabled}) {
        return (
            <CalendarButton
                key={month}
                label={month}
                selected={selected}
                disabled={disabled}
                onClick={() => this.updateDate('month', month)}/>
        )
    }

    renderDays() {
        return (

                {this.renderWeekDaysLabels()}
                {this.renderDaysOfMonth()}

        )
    }

    renderWeekDaysLabels() {
        return moment.weekdaysShort().map(weekday =>

        )
    }

    renderDaysOfMonth() {
        const {date} = this.state
        const {startDate, endDate} = this.props
        const firstOfMonth = moment(date).startOf('month')
        const firstToRender = moment(firstOfMonth).startOf('week')
        const lastOfMonth = moment(date).endOf('month')
        const firstDay = date.isSame(startDate, 'month') ? startDate.date() : 1
        const lastDate = date.isSame(endDate, 'month') ? endDate : lastOfMonth
        const lastDay = lastDate.date()
        const lastToRender = moment(lastOfMonth).endOf('week')
        const indexOffset = firstOfMonth.day() - 1
        const daysToRender = lastToRender.diff(firstToRender, 'days') + 1
        const firstIndex = firstDay + indexOffset
        const lastIndex = lastDay + indexOffset
        return _.times(daysToRender,
            i => this.renderMonthDay({
                date,
                i,
                disabled: i < firstIndex || i > lastIndex,
                firstToRender
            })
        )
    }

    renderMonthDay({date, i, firstToRender, disabled}) {
        const buttonDate = moment(firstToRender).add(i, 'day')
        const dayOfMonth = buttonDate.format('DD')
        return (
            <CalendarButton
                key={i}
                label={dayOfMonth}
                selected={buttonDate.isSame(date, 'day')}
                disabled={disabled}
                onClick={() => this.updateDate('date', dayOfMonth)}/>
        )
    }

    renderButtons() {
        return this.isDirty()
            ? <React.Fragment>
                <Panel.Buttons.Cancel onClick={() => this.close()}/>
                <Panel.Buttons.Select onClick={() => this.select()}/>
            </React.Fragment>
            : <Panel.Buttons.Close onClick={() => this.close()}/>
    }

    componentDidMount() {
        const {date} = this.props
        this.setState({
            date: this.toValidRange(date)
        })
    }

    updateDate(unit, value) {
        this.setState(prevState => ({
            date: this.toValidRange(prevState.date.set(unit, value))
        }))

    }

    toValidRange(date) {
        const {startDate, endDate} = this.props
        return constrainDate(date, startDate, endDate)
    }

    select() {
        const {onSelect} = this.props
        this.isDirty() && onSelect && onSelect(this.state.date)
        this.close()
    }

    close() {
        const {activatable} = this.props
        activatable.deactivate()
    }

    isDirty() {
        return !this.props.date.isSame(this.state.date, 'day')
    }
}

const DatePickerPanel = compose(
    _DatePickerPanel,
    activatable({
        id: ({id}) => id,
        policy: () => ({_: 'allow'}),
        alwaysAllow: true
    })
)

class CalendarButton extends Component {
    render() {
        const {label, selected, disabled, onClick} = this.props
        return (
            <Button
                chromeless={!selected}
                look={selected ? 'highlight' : 'transparent'}
                label={label}
                disabled={disabled}
                onClick={onClick}
            />
        )
    }
}

CalendarButton.propTypes = {
    className: PropTypes.any,
    label: PropTypes.any,
    selected: PropTypes.any,
    onClick: PropTypes.any
}
<|endoftext|>