<commit_before>var gulp = require('gulp'),
    gutil = require('gulp-util'),
    jscs = require('gulp-jscs'),
    jshint = require('gulp-jshint'),
    cached = require('gulp-cached'),
    coverage = require('gulp-jsx-coverage'),
    fs = require('fs'),
    buffer = require('vinyl-buffer'),
    source = require('vinyl-source-stream'),
    aliasify = require('aliasify'),
    babelify = require('babelify'),
    browserify = require('browserify'),
    babel = require('gulp-babel'),
    uglify = require('gulp-uglify'),
    nodemon = require('nodemon'),
    browserSync = require('browser-sync'),
    serverStarted = false,

// These configs will be exported and you can overrides them
configs = {
    // files to jshint and jscs
    lint_files: ['actions/*.js', 'stores/*.js', 'components/*.jsx', 'fluxexapp.js'],

    // All js/css files will be writen here
    static_dir: 'static/',

    // Major script to start your express server and mount fluxex application
    mainjs: require(process.cwd() + '/package.json').main,

    // Your fluxex application defination
    appjs: process.cwd() + '/fluxexapp.js',

    // wait time after your bundle writen then trigger nodemon restart
    nodemon_restart_delay: 200,

    // wait time after your server start then trigger browserSync reload
    nodemon_delay: 2000,

    // fail the gulp task when jshint issue found
    // edit your .jshintrc | .jshintignore to refine your jshint settings
    jshint_fail: false,

    // fail the gulp task when jscs issue found
    // edit your .jscsrc to refine your jshint settings
    jscs_fail: false,

    // If you use vim and watch tasks be triggered 2 times when saving
    // You can :set nowritebackup in vim to prevent this
    // Reference: https://github.com/joyent/node/issues/3172
    gulp_watch: {debounceDelay: 500},

    // watchify config
    // refer to https://github.com/substack/watchify
    watchify: {debug: true, delay: 500},

    // aliasify config
    // refer to https://github.com/benbria/aliasify
    aliasify: {
        aliases: {
            request: 'browser-request',
            './fluxex-server': 'fluxex/lib/fluxex-client',
            './fetch-server': 'fluxex/extra/fetch-client',
            'fluxex/extra/history': 'html5-history-api',
            'fluxex/extra/polyfill': 'babelify/polyfill',
            'fluxex/extra/polyfill-ie8': 'fluxex/extra/polyfill-ie8-client',
            'core-js/shim': 'core-js/client/shim',
            'body-parser': 'fluxex/extra/dummy'
        }
    },

    // uglify config
    // refer to https://github.com/mishoo/UglifyJS2
    uglify: undefined,

    // babel config
    // refer to http://babeljs.io/docs/usage/options/
    babel: {
        optional: ['runtime']
    },

    // babelify config, will be merged with babel config
    // refer to https://github.com/babel/babelify
    babelify: {
        // Note: for babelify this is regex, but for babel this is glob
        ignore: /node_modules/,
        extensions: ['.js', '.jsx']
    },

    // tests + coverage configs
    test_coverage: {
        // gulp-jsx-coverage config
        // refer to https://github.com/zordius/gulp-jsx-coverage
        default: {
            src: ['test/**/*.js', 'test/components/*.jsx', 'test/components/*.js'],
            istanbul: {
                coverageVariable: '__FLUXEX_COVERAGE__',
                exclude: /node_modules\/|test\//
            },
            coverage: {
                directory: 'coverage'
            },
            mocha: {},
            babel: {
                sourceMap: 'inline'
            },
            coffee: {
                sourceMap: true
            }
        },

        // for manual testing
        console: {
            coverage: {
                reporters: ['text-summary']
            },
            mocha: {
                reporter: 'spec'
            }
        },

        // for CI testing (save reports to files)
        report: {
            coverage: {
                reporters: ['lcov', 'json']
            },
            mocha: {
                reporter: 'mocha-jenkins-reporter'
            }
        }
    }
},

restartNodemon = function () {
    setTimeout(function () {
        nodemon.emit('restart');
    }, configs.nodemon_restart_delay);
},

buildLintTask = function (task) {
    if (configs.github) {
        task = task.pipe(require('gulp-github')(configs.github));
    }

    if (configs.jshint_fail) {
        task = task.pipe(('object' === typeof configs.jshint_fail) ? configs.jshint_fail : jshint.reporter('fail'));
    }

    return task;
},

// Do testing tasks
getTestingTask = function (options) {
    var cfg = JSON.parse(JSON.stringify(configs.test_coverage.default));

    cfg.istanbul.exclude = configs.test_coverage.default.istanbul.exclude;
    cfg.coverage.reporters = options.coverage.reporters;
    cfg.mocha = options.mocha;
    cfg.cleanup = configs.test_coverage.default.cleanup;

    return coverage.createTask(cfg);
},

handleJSCSError = function (E) {
    if (!configs.jscs_fail) {
        return;
    }

    if ('function' === typeof configs.jscs_fail) {
        return configs.jscs_fail(E);
    }

    this.emit('error', E);
},

bundleAll = function (b, noSave) {
    var B = b.bundle()
    .on('error', function (E) {
        gutil.log('[browserify ERROR]', gutil.colors.red(E));
    });

    if (!noSave) {
        B.pipe(source('main.js'))
        .pipe(gulp.dest(configs.static_dir + 'js/'))
        .on('end', restartNodemon);
    }

    return B;
},

buildApp = function (watch, fullpath, nosave) {
    var b = browserify(configs.appjs, {
        cache: {},
        packageCache: {},
        standalone: 'Fluxex',
        fullPaths: fullpath ? true : false,
        debug: watch
    });

    b.transform(babelify.configure(Object.assign({}, configs.babel, configs.babelify)), {global: true});
    b.transform(aliasify.configure(configs.aliasify), {global: true});

    if (watch) {
        b = require('watchify')(b, configs.watchify);
        b.on('update', function (F) {
            gutil.log('[browserify] ' + F[0] + ' updated');
            bundleAll(b);
        });
    }

    return bundleAll(b, nosave);
};

// GULP TASK - build minified bundle file
gulp.task('build_app', function () {
    return buildApp(false, false, true)
        .pipe(source('main.js'))
        .pipe(buffer())
        .pipe(uglify(configs.uglify))
        .pipe(gulp.dest(configs.static_dir + 'js/'));
});

// GULP TASK - create disc analyze report file
gulp.task('disc_app', function () {
    return buildApp(false, true, true)
        .pipe(require('disc')())
        .pipe(fs.createWriteStream(configs.static_dir + 'disc.html'));
});

// GULP TASK - watch and build bundle file for develop
gulp.task('watch_app', function () {
    return buildApp(true, true);
});

// GULP TASK - watch and lint js files for develop
gulp.task('watch_js', ['lint_js'], function () {
    gulp.watch(configs.lint_files.js, configs.gulp_watch, ['lint_js']);
});

// GULP TASK - lint js files for develop
gulp.task('lint_js', function () {
    return buildLintTask(
        gulp.src(configs.lint_files)
        .pipe(cached('jshint'))
        .pipe(babel(Object.assign({sourceMap: true}, configs.babel)))
        .pipe(jscs()).on('error', handleJSCSError)
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'))
    );
});

// GULP TASK - watch main and app js to lint then restart nodemon
gulp.task('watch_server', ['lint_server'], function () {
    gulp.watch([configs.mainjs, configs.appjs], configs.gulp_watch, ['lint_server'])
    .on('change', function (E) {
        if (E.path !== configs.appjs) {
            nodemon.emit('restart');
        }
    });
});

// GULP TASK - lint main and app js files
gulp.task('lint_server', function () {
    return gulp.src([configs.mainjs, configs.appjs])
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

// GULP TASK - start nodemon server and browserSync proxy
gulp.task('nodemon_server', ['watch_js', 'watch_app', 'watch_server'], function () {
    nodemon({
        ignore: '*',
        script: configs.mainjs,
        ext: 'do_not_watch'
    })
    .on('log', function (log) {
        gutil.log(log.colour);
    })
    .on('start', function () {
        if (serverStarted) {
            setTimeout(browserSync.reload, configs.nodemon_delay);
        } else {
            browserSync.init(null, {
                proxy: 'http://localhost:3000',
                files: [configs.static_dir + 'css/*.css'],
                port: 3001,
                online: false,
                open: false,
                snippetOptions: {
                    rule: {
                        match: /<\/html>$/,
                        fn: function (s, m) {
                            return m + s;
                        }
                    }
                }
            });

            serverStarted = true;
        }
    });
});

// GULP TASK - watch tests files then execute test
gulp.task('watch_tests', ['test_app'], function () {
    gulp.watch([
        configs.test_coverage.default.src,
        configs.lint_files
    ], ['test_app']);
});

// GULP TASK - execute mocha tests, output to console
gulp.task('test_app', function (cb) {
    getTestingTask(configs.test_coverage.console)(cb).on('error', function (E) {
        if (E.stack || E.showStack) {
            console.warn(E.stack);
        } else {
            console.warn(E);
        }
    }).on('end', function () {
        cb(); // prevent failed ending
    });
});

// GULP TASK - execute mocha tests, output to file
gulp.task('save_test_app', function () {
    return getTestingTask(configs.test_coverage.report)();
});

// GULP TASKS - alias and depdency
gulp.task('develop', ['nodemon_server']);
gulp.task('lint_all', ['lint_server', 'lint_js']);
gulp.task('buildall', ['lint_all', 'build_app']);
gulp.task('default',['buildall']);

module.exports = configs;
<commit_msg>fix missing lint issue<commit_after>var gulp = require('gulp'),
    gutil = require('gulp-util'),
    jscs = require('gulp-jscs'),
    jshint = require('gulp-jshint'),
    cached = require('gulp-cached'),
    coverage = require('gulp-jsx-coverage'),
    fs = require('fs'),
    buffer = require('vinyl-buffer'),
    source = require('vinyl-source-stream'),
    aliasify = require('aliasify'),
    babelify = require('babelify'),
    browserify = require('browserify'),
    babel = require('gulp-babel'),
    uglify = require('gulp-uglify'),
    nodemon = require('nodemon'),
    browserSync = require('browser-sync'),
    serverStarted = false,

// These configs will be exported and you can overrides them
configs = {
    // files to jshint and jscs
    lint_files: ['actions/*.js', 'stores/*.js', 'components/*.jsx', 'fluxexapp.js'],

    // All js/css files will be writen here
    static_dir: 'static/',

    // Major script to start your express server and mount fluxex application
    mainjs: require(process.cwd() + '/package.json').main,

    // Your fluxex application defination
    appjs: process.cwd() + '/fluxexapp.js',

    // wait time after your bundle writen then trigger nodemon restart
    nodemon_restart_delay: 200,

    // wait time after your server start then trigger browserSync reload
    nodemon_delay: 2000,

    // fail the gulp task when jshint issue found
    // edit your .jshintrc | .jshintignore to refine your jshint settings
    jshint_fail: false,

    // fail the gulp task when jscs issue found
    // edit your .jscsrc to refine your jshint settings
    jscs_fail: false,

    // If you use vim and watch tasks be triggered 2 times when saving
    // You can :set nowritebackup in vim to prevent this
    // Reference: https://github.com/joyent/node/issues/3172
    gulp_watch: {debounceDelay: 500},

    // watchify config
    // refer to https://github.com/substack/watchify
    watchify: {debug: true, delay: 500},

    // aliasify config
    // refer to https://github.com/benbria/aliasify
    aliasify: {
        aliases: {
            request: 'browser-request',
            './fluxex-server': 'fluxex/lib/fluxex-client',
            './fetch-server': 'fluxex/extra/fetch-client',
            'fluxex/extra/history': 'html5-history-api',
            'fluxex/extra/polyfill': 'babelify/polyfill',
            'fluxex/extra/polyfill-ie8': 'fluxex/extra/polyfill-ie8-client',
            'core-js/shim': 'core-js/client/shim',
            'body-parser': 'fluxex/extra/dummy'
        }
    },

    // uglify config
    // refer to https://github.com/mishoo/UglifyJS2
    uglify: undefined,

    // babel config
    // refer to http://babeljs.io/docs/usage/options/
    babel: {
        optional: ['runtime']
    },

    // babelify config, will be merged with babel config
    // refer to https://github.com/babel/babelify
    babelify: {
        // Note: for babelify this is regex, but for babel this is glob
        ignore: /node_modules/,
        extensions: ['.js', '.jsx']
    },

    // tests + coverage configs
    test_coverage: {
        // gulp-jsx-coverage config
        // refer to https://github.com/zordius/gulp-jsx-coverage
        default: {
            src: ['test/**/*.js', 'test/components/*.jsx', 'test/components/*.js'],
            istanbul: {
                coverageVariable: '__FLUXEX_COVERAGE__',
                exclude: /node_modules\/|test\//
            },
            coverage: {
                directory: 'coverage'
            },
            mocha: {},
            babel: {
                sourceMap: 'inline'
            },
            coffee: {
                sourceMap: true
            }
        },

        // for manual testing
        console: {
            coverage: {
                reporters: ['text-summary']
            },
            mocha: {
                reporter: 'spec'
            }
        },

        // for CI testing (save reports to files)
        report: {
            coverage: {
                reporters: ['lcov', 'json']
            },
            mocha: {
                reporter: 'mocha-jenkins-reporter'
            }
        }
    }
},

restartNodemon = function () {
    setTimeout(function () {
        nodemon.emit('restart');
    }, configs.nodemon_restart_delay);
},

buildLintTask = function (task) {
    if (configs.github) {
        task = task.pipe(require('gulp-github')(configs.github));
    }

    if (configs.jshint_fail) {
        task = task.pipe(('object' === typeof configs.jshint_fail) ? configs.jshint_fail : jshint.reporter('fail'));
    }

    return task;
},

// Do testing tasks
getTestingTask = function (options) {
    var cfg = JSON.parse(JSON.stringify(configs.test_coverage.default));

    cfg.istanbul.exclude = configs.test_coverage.default.istanbul.exclude;
    cfg.coverage.reporters = options.coverage.reporters;
    cfg.mocha = options.mocha;
    cfg.cleanup = configs.test_coverage.default.cleanup;

    return coverage.createTask(cfg);
},

handleJSCSError = function (E) {
    if (!configs.jscs_fail) {
        return;
    }

    if ('function' === typeof configs.jscs_fail) {
        return configs.jscs_fail(E);
    }

    this.emit('error', E);
},

bundleAll = function (b, noSave) {
    var B = b.bundle()
    .on('error', function (E) {
        gutil.log('[browserify ERROR]', gutil.colors.red(E));
    });

    if (!noSave) {
        B.pipe(source('main.js'))
        .pipe(gulp.dest(configs.static_dir + 'js/'))
        .on('end', restartNodemon);
    }

    return B;
},

buildApp = function (watch, fullpath, nosave) {
    var b = browserify(configs.appjs, {
        cache: {},
        packageCache: {},
        standalone: 'Fluxex',
        fullPaths: fullpath ? true : false,
        debug: watch
    });

    b.transform(babelify.configure(Object.assign({}, configs.babel, configs.babelify)), {global: true});
    b.transform(aliasify.configure(configs.aliasify), {global: true});

    if (watch) {
        b = require('watchify')(b, configs.watchify);
        b.on('update', function (F) {
            gutil.log('[browserify] ' + F[0] + ' updated');
            bundleAll(b);
        });
    }

    return bundleAll(b, nosave);
};

// GULP TASK - build minified bundle file
gulp.task('build_app', function () {
    return buildApp(false, false, true)
        .pipe(source('main.js'))
        .pipe(buffer())
        .pipe(uglify(configs.uglify))
        .pipe(gulp.dest(configs.static_dir + 'js/'));
});

// GULP TASK - create disc analyze report file
gulp.task('disc_app', function () {
    return buildApp(false, true, true)
        .pipe(require('disc')())
        .pipe(fs.createWriteStream(configs.static_dir + 'disc.html'));
});

// GULP TASK - watch and build bundle file for develop
gulp.task('watch_app', function () {
    return buildApp(true, true);
});

// GULP TASK - watch and lint js files for develop
gulp.task('watch_js', ['lint_js'], function () {
    gulp.watch(configs.lint_files, configs.gulp_watch, ['lint_js']);
});

// GULP TASK - lint js files for develop
gulp.task('lint_js', function () {
    return buildLintTask(
        gulp.src(configs.lint_files)
        .pipe(cached('jshint'))
        .pipe(babel(Object.assign({sourceMap: true}, configs.babel)))
        .pipe(jscs()).on('error', handleJSCSError)
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'))
    );
});

// GULP TASK - watch main and app js to lint then restart nodemon
gulp.task('watch_server', ['lint_server'], function () {
    gulp.watch([configs.mainjs, configs.appjs], configs.gulp_watch, ['lint_server'])
    .on('change', function (E) {
        if (E.path !== configs.appjs) {
            nodemon.emit('restart');
        }
    });
});

// GULP TASK - lint main and app js files
gulp.task('lint_server', function () {
    return gulp.src([configs.mainjs, configs.appjs])
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

// GULP TASK - start nodemon server and browserSync proxy
gulp.task('nodemon_server', ['watch_js', 'watch_app', 'watch_server'], function () {
    nodemon({
        ignore: '*',
        script: configs.mainjs,
        ext: 'do_not_watch'
    })
    .on('log', function (log) {
        gutil.log(log.colour);
    })
    .on('start', function () {
        if (serverStarted) {
            setTimeout(browserSync.reload, configs.nodemon_delay);
        } else {
            browserSync.init(null, {
                proxy: 'http://localhost:3000',
                files: [configs.static_dir + 'css/*.css'],
                port: 3001,
                online: false,
                open: false,
                snippetOptions: {
                    rule: {
                        match: /<\/html>$/,
                        fn: function (s, m) {
                            return m + s;
                        }
                    }
                }
            });

            serverStarted = true;
        }
    });
});

// GULP TASK - watch tests files then execute test
gulp.task('watch_tests', ['test_app'], function () {
    gulp.watch([
        configs.test_coverage.default.src,
        configs.lint_files
    ], ['test_app']);
});

// GULP TASK - execute mocha tests, output to console
gulp.task('test_app', function (cb) {
    getTestingTask(configs.test_coverage.console)(cb).on('error', function (E) {
        if (E.stack || E.showStack) {
            console.warn(E.stack);
        } else {
            console.warn(E);
        }
    }).on('end', function () {
        cb(); // prevent failed ending
    });
});

// GULP TASK - execute mocha tests, output to file
gulp.task('save_test_app', function () {
    return getTestingTask(configs.test_coverage.report)();
});

// GULP TASKS - alias and depdency
gulp.task('develop', ['nodemon_server']);
gulp.task('lint_all', ['lint_server', 'lint_js']);
gulp.task('buildall', ['lint_all', 'build_app']);
gulp.task('default',['buildall']);

module.exports = configs;
<|endoftext|>


<commit_before>const { dialog } = nodeRequire('electron').remote;

const readline = nodeRequire('readline');
const fs = nodeRequire('fs');

var rf_data = {};

// 把斜杠换成空白
function handle_one_line_data(anArray) {

    for (var k = 0, length = anArray.length; k < length; k++) {
        if (anArray[k] == '\\') {
            anArray[k] = ''
        }
    }
    return anArray

}

function renderTestcaseNode(filepathlist) {

    $.each(filepathlist, function (key, value) {

        var rftype = '';// case、keyword、setting、variable
        var filetype = 'resource';// 2种类型：suite和resource
        var parent_node_id = value[1].toString();
        var file_data = {};

        var case_name_id_tmp = "";
        var case_id_count = 1;
        var pre_line = '';

        rl = readline.createInterface({
            input: fs.createReadStream(value[0])
        });

        rl.on('line', (line) => {

            if (line == '*** Test Cases ***') {
                rftype = 'testcase';
                filetype = 'suite';
            } else if (line == '*** Keywords ***') {
                rftype = 'keywords';
            } else if (line == '*** Settings ***') {
                rftype = 'settings';
                case_name_id_tmp = 'settings';
                file_data[case_name_id_tmp] = [
                    ['Documentation', '', '', '', ''],
                    ['Suite Setup', '', '', '', ''],
                    ['Suite Teardown', '', '', '', ''],
                    ['Test Setup', '', '', '', ''],
                    ['Test Teardown', '', '', '', ''],
                    ['Test Template', '', '', '', ''],
                    ['Test Timeout', '', '', '', ''],
                    ['Force Tags', '', '', '', ''],
                    ['Default Tags', '', '', '', '']
                ];
            } else if (line == '*** Variables ***') {
                rftype = 'variables';
                case_name_id_tmp = 'variables';
                file_data[case_name_id_tmp] = [];
            } else {

                if (rftype == 'testcase') {
                    if (line[0] != ' ' && line.indexOf('***') != 0 && line) {
                        var case_id = parent_node_id + '0' + case_id_count++;
                        zTree.addNodes(zTree.getNodeByParam("id", parent_node_id), { id: case_id, name: line, isParent: false, iconSkin: "testcase" }, true);
                        case_name_id_tmp = line + '_' + case_id;
                        file_data[case_name_id_tmp] = [];
                    } else {
                        if (case_name_id_tmp) {
                            file_data[case_name_id_tmp].push(handle_one_line_data($.trim(line).split('    ')));
                        }

                    }
                } else if (rftype == 'keywords') {
                    if (line[0] != ' ' && line.indexOf('***') != 0 && line) {
                        var case_id = parent_node_id + '0' + case_id_count++;
                        if (value[2] == '__init__') {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id).getParentNode(), { id: case_id, name: line, isParent: false, iconSkin: "keyword", nocheck: true }, true);
                        } else {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id), { id: case_id, name: line, isParent: false, iconSkin: "keyword", nocheck: true }, true);
                        }
                        case_name_id_tmp = line + '_' + case_id;
                        file_data[case_name_id_tmp] = [];
                    } else {
                        if (case_name_id_tmp) {
                            file_data[case_name_id_tmp].push(handle_one_line_data($.trim(line).split('    ')));
                        }

                    }
                } else if (rftype == 'variables') {

                    if ($.trim(line)) {

                        var variable_line = $.trim(line).split('}');

                        var variable_name = variable_line.shift() + '}';
                        var variable_other = variable_line.join("}");
                        var variable_data = $.trim(variable_other).split('    ');
                        variable_data.splice(0, 0, variable_name);

                        var case_id = parent_node_id + '0' + case_id_count++;

                        if (value[2] == '__init__') {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id).getParentNode(), { id: case_id, name: variable_name, isParent: false, iconSkin: "variable", nocheck: true }, true);
                        } else {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id), { id: case_id, name: variable_name, isParent: false, iconSkin: "variable", nocheck: true }, true);

                        }
                        case_name_id_tmp = 'variables';
                        if (case_name_id_tmp) {
                            file_data[case_name_id_tmp].push(variable_data);
                        }
                    }

                } else if (rftype == 'settings') {

                    if ($.trim(line)) {
                        console.log(rftype, line);
                        var settings_type = $.trim($.trim(line).substring(0, 17));
                        var settings_args = $.trim($.trim(line).substring(18)).split('    ');
                        settings_args.splice(0, 0, settings_type);

                        case_name_id_tmp = 'settings';

                        console.log(settings_type, settings_args);

                        if (case_name_id_tmp) {

                            switch (settings_type) {
                                case 'Documentation':
                                    file_data[case_name_id_tmp][0] = settings_args;
                                    pre_line = 0;
                                    break;
                                case 'Suite Setup':
                                    file_data[case_name_id_tmp][1] = settings_args;
                                    pre_line = 1;
                                    break;
                                case 'Suite Teardown':
                                    file_data[case_name_id_tmp][2] = settings_args;
                                    pre_line = 2;
                                    break;
                                case 'Test Setup':
                                    file_data[case_name_id_tmp][3] = settings_args;
                                    pre_line = 3;
                                    break;
                                case 'Test Teardown':
                                    file_data[case_name_id_tmp][4] = settings_args;
                                    pre_line = 4;
                                    break;
                                case 'Test Template':
                                    file_data[case_name_id_tmp][5] = settings_args;
                                    pre_line = 5;
                                    break;
                                case 'Test Timeout':
                                    file_data[case_name_id_tmp][6] = settings_args;
                                    pre_line = 6;
                                    break;
                                case 'Force Tags':
                                    file_data[case_name_id_tmp][7] = settings_args;
                                    pre_line = 7;
                                    break;
                                case 'Default Tags':
                                    file_data[case_name_id_tmp][8] = settings_args;
                                    pre_line = 8;
                                    break;
                                case '...':
                                    console.log(pre_line, settings_args);
                                    settings_args.shift();
                                    file_data[case_name_id_tmp][pre_line].push.apply(file_data[case_name_id_tmp][pre_line], settings_args);
                                    break;
                                default:
                                    file_data[case_name_id_tmp].push(settings_args);
                                    break;
                            }

                            // file_data[case_name_id_tmp].push(settings_args);
                        }

                    }

                }

            }
        });

        rl.on('close', function () {
            if (filetype == 'resource') {
                var update_node = zTree.getNodeByParam("id", parent_node_id);
                update_node.iconSkin = 'resource';
                zTree.updateNode(update_node);
            }

            console.log(file_data, value[2]);

            // __init__ 文件，setting种没有 Test Template、Default Tags、Test Timeout
            if (value[2] == '__init__') {

                file_data['settings'].splice(8, 1);
                file_data['settings'].splice(6, 1);
                file_data['settings'].splice(5, 1);

                var node_id = zTree.getNodeByParam("id", parent_node_id).getParentNode().id
                rf_data[value[2] + "_" + node_id] = file_data;

                // 移除__init__ 文件的节点
                zTree.removeNode(zTree.getNodeByParam("id", parent_node_id));

            } else {

                rf_data[value[2] + "_" + value[1]] = file_data;

            }

            // 对resource文件，settings中只有一格document，其他去掉
            if (filetype == 'resource' && value[2] != '__init__') {

                if (file_data.hasOwnProperty('settings')) {
                    file_data['settings'].splice(8, 1);
                    file_data['settings'].splice(7, 1);
                    file_data['settings'].splice(6, 1);
                    file_data['settings'].splice(5, 1);
                    file_data['settings'].splice(4, 1);
                    file_data['settings'].splice(3, 1);
                    file_data['settings'].splice(2, 1);
                    file_data['settings'].splice(1, 1);

                }

            }

            // 对没有settings的文件，添加settings

            if (filetype == 'resource' && !file_data.hasOwnProperty('settings')) {
                file_data['settings'] = [
                    ['Documentation', '', '', '', '']
                ];

            } else if (filetype == 'suite' && !file_data.hasOwnProperty('settings')) {

                file_data['settings'] = [
                    ['Documentation', '', '', '', ''],
                    ['Suite Setup', '', '', '', ''],
                    ['Suite Teardown', '', '', '', ''],
                    ['Test Setup', '', '', '', ''],
                    ['Test Teardown', '', '', '', ''],
                    ['Test Template', '', '', '', ''],
                    ['Test Timeout', '', '', '', ''],
                    ['Force Tags', '', '', '', ''],
                    ['Default Tags', '', '', '', '']
                ];

            } else if (value[2] == '__init__' && !file_data.hasOwnProperty('settings')) {
                file_data['settings'] = [
                    ['Documentation', '', '', '', ''],
                    ['Suite Setup', '', '', '', ''],
                    ['Suite Teardown', '', '', '', ''],
                    ['Test Setup', '', '', '', ''],
                    ['Test Teardown', '', '', '', ''],
                    ['Force Tags', '', '', '', '']
                ];
            }

        });

    });
}

function openDirectory() {

    var directory = dialog.showOpenDialog({ properties: ['openDirectory'] });
    if (directory) {
        var dataArr = get_workspace_data(directory[0]);
        zTree.destroy();
        UITree.init();
        zTree.addNodes(null, dataArr[0])

        renderTestcaseNode(dataArr[1]);

    }

}

<commit_msg>修复 解析init为空文件时候报错的问题<commit_after>const { dialog } = nodeRequire('electron').remote;

const readline = nodeRequire('readline');
const fs = nodeRequire('fs');

var rf_data = {};

// 把斜杠换成空白
function handle_one_line_data(anArray) {

    for (var k = 0, length = anArray.length; k < length; k++) {
        if (anArray[k] == '\\') {
            anArray[k] = ''
        }
    }
    return anArray

}

function renderTestcaseNode(filepathlist) {

    $.each(filepathlist, function (key, value) {

        var rftype = '';// case、keyword、setting、variable
        var filetype = 'resource';// 2种类型：suite和resource
        var parent_node_id = value[1].toString();
        var file_data = {};

        var case_name_id_tmp = "";
        var case_id_count = 1;
        var pre_line = '';

        rl = readline.createInterface({
            input: fs.createReadStream(value[0])
        });

        rl.on('line', (line) => {

            if (line == '*** Test Cases ***') {
                rftype = 'testcase';
                filetype = 'suite';
            } else if (line == '*** Keywords ***') {
                rftype = 'keywords';
            } else if (line == '*** Settings ***') {
                rftype = 'settings';
                case_name_id_tmp = 'settings';
                file_data[case_name_id_tmp] = [
                    ['Documentation', '', '', '', ''],
                    ['Suite Setup', '', '', '', ''],
                    ['Suite Teardown', '', '', '', ''],
                    ['Test Setup', '', '', '', ''],
                    ['Test Teardown', '', '', '', ''],
                    ['Test Template', '', '', '', ''],
                    ['Test Timeout', '', '', '', ''],
                    ['Force Tags', '', '', '', ''],
                    ['Default Tags', '', '', '', '']
                ];
            } else if (line == '*** Variables ***') {
                rftype = 'variables';
                case_name_id_tmp = 'variables';
                file_data[case_name_id_tmp] = [];
            } else {

                if (rftype == 'testcase') {
                    if (line[0] != ' ' && line.indexOf('***') != 0 && line) {
                        var case_id = parent_node_id + '0' + case_id_count++;
                        zTree.addNodes(zTree.getNodeByParam("id", parent_node_id), { id: case_id, name: line, isParent: false, iconSkin: "testcase" }, true);
                        case_name_id_tmp = line + '_' + case_id;
                        file_data[case_name_id_tmp] = [];
                    } else {
                        if (case_name_id_tmp) {
                            file_data[case_name_id_tmp].push(handle_one_line_data($.trim(line).split('    ')));
                        }

                    }
                } else if (rftype == 'keywords') {
                    if (line[0] != ' ' && line.indexOf('***') != 0 && line) {
                        var case_id = parent_node_id + '0' + case_id_count++;
                        if (value[2] == '__init__') {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id).getParentNode(), { id: case_id, name: line, isParent: false, iconSkin: "keyword", nocheck: true }, true);
                        } else {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id), { id: case_id, name: line, isParent: false, iconSkin: "keyword", nocheck: true }, true);
                        }
                        case_name_id_tmp = line + '_' + case_id;
                        file_data[case_name_id_tmp] = [];
                    } else {
                        if (case_name_id_tmp) {
                            file_data[case_name_id_tmp].push(handle_one_line_data($.trim(line).split('    ')));
                        }

                    }
                } else if (rftype == 'variables') {

                    if ($.trim(line)) {

                        var variable_line = $.trim(line).split('}');

                        var variable_name = variable_line.shift() + '}';
                        var variable_other = variable_line.join("}");
                        var variable_data = $.trim(variable_other).split('    ');
                        variable_data.splice(0, 0, variable_name);

                        var case_id = parent_node_id + '0' + case_id_count++;

                        if (value[2] == '__init__') {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id).getParentNode(), { id: case_id, name: variable_name, isParent: false, iconSkin: "variable", nocheck: true }, true);
                        } else {
                            zTree.addNodes(zTree.getNodeByParam("id", parent_node_id), { id: case_id, name: variable_name, isParent: false, iconSkin: "variable", nocheck: true }, true);

                        }
                        case_name_id_tmp = 'variables';
                        if (case_name_id_tmp) {
                            file_data[case_name_id_tmp].push(variable_data);
                        }
                    }

                } else if (rftype == 'settings') {

                    if ($.trim(line)) {
                        console.log(rftype, line);
                        var settings_type = $.trim($.trim(line).substring(0, 17));
                        var settings_args = $.trim($.trim(line).substring(18)).split('    ');
                        settings_args.splice(0, 0, settings_type);

                        case_name_id_tmp = 'settings';

                        console.log(settings_type, settings_args);

                        if (case_name_id_tmp) {

                            switch (settings_type) {
                                case 'Documentation':
                                    file_data[case_name_id_tmp][0] = settings_args;
                                    pre_line = 0;
                                    break;
                                case 'Suite Setup':
                                    file_data[case_name_id_tmp][1] = settings_args;
                                    pre_line = 1;
                                    break;
                                case 'Suite Teardown':
                                    file_data[case_name_id_tmp][2] = settings_args;
                                    pre_line = 2;
                                    break;
                                case 'Test Setup':
                                    file_data[case_name_id_tmp][3] = settings_args;
                                    pre_line = 3;
                                    break;
                                case 'Test Teardown':
                                    file_data[case_name_id_tmp][4] = settings_args;
                                    pre_line = 4;
                                    break;
                                case 'Test Template':
                                    file_data[case_name_id_tmp][5] = settings_args;
                                    pre_line = 5;
                                    break;
                                case 'Test Timeout':
                                    file_data[case_name_id_tmp][6] = settings_args;
                                    pre_line = 6;
                                    break;
                                case 'Force Tags':
                                    file_data[case_name_id_tmp][7] = settings_args;
                                    pre_line = 7;
                                    break;
                                case 'Default Tags':
                                    file_data[case_name_id_tmp][8] = settings_args;
                                    pre_line = 8;
                                    break;
                                case '...':
                                    console.log(pre_line, settings_args);
                                    settings_args.shift();
                                    file_data[case_name_id_tmp][pre_line].push.apply(file_data[case_name_id_tmp][pre_line], settings_args);
                                    break;
                                default:
                                    file_data[case_name_id_tmp].push(settings_args);
                                    break;
                            }

                            // file_data[case_name_id_tmp].push(settings_args);
                        }

                    }

                }

            }
        });

        rl.on('close', function () {
            if (filetype == 'resource') {
                var update_node = zTree.getNodeByParam("id", parent_node_id);
                update_node.iconSkin = 'resource';
                zTree.updateNode(update_node);
            }

            console.log(file_data, value[2]);

            // __init__ 文件，setting种没有 Test Template、Default Tags、Test Timeout
            if (value[2] == '__init__') {

                if (file_data.hasOwnProperty('settings')) {
                    file_data['settings'].splice(8, 1);
                    file_data['settings'].splice(6, 1);
                    file_data['settings'].splice(5, 1);
                }
                var node_id = zTree.getNodeByParam("id", parent_node_id).getParentNode().id
                rf_data[value[2] + "_" + node_id] = file_data;

                // 移除__init__ 文件的节点
                zTree.removeNode(zTree.getNodeByParam("id", parent_node_id));

            } else {

                rf_data[value[2] + "_" + value[1]] = file_data;

            }

            // 对resource文件，settings中只有一格document，其他去掉
            if (filetype == 'resource' && value[2] != '__init__') {

                if (file_data.hasOwnProperty('settings')) {
                    file_data['settings'].splice(8, 1);
                    file_data['settings'].splice(7, 1);
                    file_data['settings'].splice(6, 1);
                    file_data['settings'].splice(5, 1);
                    file_data['settings'].splice(4, 1);
                    file_data['settings'].splice(3, 1);
                    file_data['settings'].splice(2, 1);
                    file_data['settings'].splice(1, 1);

                }

            }

            // 对没有settings的文件，添加settings

            if (filetype == 'resource' && !file_data.hasOwnProperty('settings')) {
                file_data['settings'] = [
                    ['Documentation', '', '', '', '']
                ];

            } else if (filetype == 'suite' && !file_data.hasOwnProperty('settings')) {

                file_data['settings'] = [
                    ['Documentation', '', '', '', ''],
                    ['Suite Setup', '', '', '', ''],
                    ['Suite Teardown', '', '', '', ''],
                    ['Test Setup', '', '', '', ''],
                    ['Test Teardown', '', '', '', ''],
                    ['Test Template', '', '', '', ''],
                    ['Test Timeout', '', '', '', ''],
                    ['Force Tags', '', '', '', ''],
                    ['Default Tags', '', '', '', '']
                ];

            } else if (value[2] == '__init__' && !file_data.hasOwnProperty('settings')) {
                file_data['settings'] = [
                    ['Documentation', '', '', '', ''],
                    ['Suite Setup', '', '', '', ''],
                    ['Suite Teardown', '', '', '', ''],
                    ['Test Setup', '', '', '', ''],
                    ['Test Teardown', '', '', '', ''],
                    ['Force Tags', '', '', '', '']
                ];
            }

        });

    });
}

function openDirectory() {

    var directory = dialog.showOpenDialog({ properties: ['openDirectory'] });
    if (directory) {
        var dataArr = get_workspace_data(directory[0]);
        zTree.destroy();
        UITree.init();
        zTree.addNodes(null, dataArr[0])

        renderTestcaseNode(dataArr[1]);

    }

}

<|endoftext|>


<commit_before>// Utils

function randomId()
{
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for(var i = 0; i < 15; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return text;
}

/**
 * Boards
 *
 * Format:
 * {
 *     name: String,
 *     description: String,
 *     lists: [String, ...],
 *
 *     owner: String,
 *     admins: [String, ...],
 *     members: [String, ...],
 *     invited: [String, ...],
 *
 *     created_at: Number,
 *     creator: String,
 *
 *     is_public: Boolean,
 *     labels: [String, ...],
 *     uri: String,
 * }
 */
Boards = new Meteor.Collection("boards");

Boards.allow({
  insert: function (userId, board) {
    return false; // no cowboy inserts -- use createBoard method
  },
  update: function (userId, boards, fields, modifier) {
    return _.all(boards, function (board) {
      if (userId !== board.owner && board.admins.indexOf(userId) == -1) {
        return false; // not the owner
      }

      var allowed = ["name", "description", "lists_id", ];
      if (_.difference(fields, allowed).length)
        return false; // tried to write to forbidden field

      // TODO: A good improvement would be to validate the type of the new
      // value of the field (and if a string, the length.) In the
      // future Meteor will have a schema system to makes that easier.
      return true;
    });
  },
  remove: function (userId, boards) {
    return ! _.any(boards, function (board) {
      // deny if not the owner
      return board.owner !== userId;
    });
  }
});

Meteor.methods({
    /**
     * Creates a new board, with three empty lists by default:
     * Todo, Doing & Done
     */
    createBoard: function (options)
    {
        if (!this.userId) {
            throw new Meteor.Error(400, "Anonymous user");
        }
        options = options || {};
        if (!(typeof options.name === "string" && options.name.length > 3 &&
            options.name.length < 140))
        {
            throw new Meteor.Error(400, "Invalid name");
        }

        var created_at = new Date().getTime();

        var uri = randomId();

        var todoListId = Lists.insert({
            board_uri: uri,
            name: "Todo",
            cards: [],
            created_at: created_at,
            creator: this.userId
        });

        var doingListId = Lists.insert({
            board_uri: uri,
            name: "Doing",
            cards: [],
            created_at: created_at,
            creator: this.userId
        });

        var doneListId = Lists.insert({
            board_uri: uri,
            name: "Done",
            cards: [],
            created_at: created_at,
            creator: this.userId
        });

        var boardId = Boards.insert({
            name: options.name,
            description: "",
            lists: [todoListId, doingListId, doneListId],
            owner: this.userId,
            admins: [this.userId],
            members: [this.userId],
            invited: [],
            created_at: created_at,
            creator: this.userId,
            is_public: true,
            labels: [],
            uri: uri
        });

        return uri;
    },

    /**
     * Creates a list in a board if you have board edit perms
     */
    createList: function (options) {
        var board = Boards.findOne(
            {$and: [
                {uri: options.board_uri},
                {members: this.userId},
            ]});

        if (!board) {
            throw new Meteor.Error(400, "Invalid permissions");
        }

        if (!(typeof options.name === "string" && options.name.length > 0
            && options.name.length < 140))
        {
            throw new Meteor.Error(400, "Invalid name");
        }

        var listId = Lists.insert({
            board_uri: options.board_uri,
            name: options.name,
            cards: [],
            created_at: new Date().getTime(),
            creator: this.userId,
        });

        Boards.update({uri: options.board_uri}, {$addToSet: {lists: listId}});
        return listId;
    },

    /**
     * Removes a list and all its cards, if you have edit permissions in the
     * board
     */
    removeList: function (options) {
        var list = Lists.findOne({_id: options.list_id});

        if (!list) {
            throw new Meteor.Error(404, "List not found");
        }

        var board = Boards.findOne(
            {$and: [
                {uri: list.board_uri},
                {members: this.userId},
            ]});

        if (!board) {
            throw new Meteor.Error(400, "Invalid permissions");
        }

        Cards.remove({board_uri: board.uri, _id: {$in: list.cards}});
        Lists.remove({_id: list._id});
    },

    /**
     * Creates a card in a board list, if you have board edit perms
     */
    createCard: function (list_id, name) {
        var board = Boards.findOne(
            {$and: [
                {lists: list_id},
                {members: this.userId},
            ]});

        if (!board) {
            throw new Meteor.Error(400, "Invalid permissions");
        }

        if (!(typeof name === "string" && name.length > 0 && name.length < 140)) {
            throw new Meteor.Error(400, "Invalid name");
        }

        var uri = randomId();

        var cardId = Cards.insert({
            board_uri: board.uri,
            name: name,
            description: "",
            members: [],
            subscribed: [],
            created_at: new Date().getTime(),
            creator: this.userId,
            uri: uri,
        });

        Lists.update({_id: list_id}, {$addToSet: {cards: cardId}});
        return uri;
    },
});

/**
 * Lists
 *
 * Format:
 * {
 *     board_uri: String,
 *     name: String,
 *     cards: [String, ...],
 *     created_at: Number,
 *     creator: String,
 * }
 */
Lists = new Meteor.Collection("lists");

Lists.allow({
    insert: function (userId, list) {
        /* use createList instead */
        return false;
    },
    update: function (userId, lists, fields, modifier) {
        return _.all(lists, function (list) {

            // For now, all members can edit lists
            var board = Boards.findOne(
                {$and: [
                    {lists: list._id},
                    {members: userId},
                ]});

            // no permissions in this board or board for this list doesn't exist
            if (!board) {
                return false;
            }

            // check all cards exist
            for (var card_id in board.cards) {
                if (Cards.find({_id: card_id}).count() != 1) {
                    return false;
                }
            }

            // check name is valid
            if (!(typeof list.name === "string" && list.name.length > 3
                && list.name.length < 140)) {
                return false;
            }

            var allowed = ["name", "cards", ];
            if (_.difference(fields, allowed).length) {
                return false; // tried to write to forbidden field
            }

            // TODO: A good improvement would be to validate the type of the new
            // value of the field (and if a string, the length.) In the
            // future Meteor will have a schema system to makes that easier.
                return true;
        });
    },
    remove: function (userId, lists) {
        // use removeList instead
        return false;
    }
});

/**
 * Cards
 *
 * Format:
 * {
 *     board_uri: String,
 *     name: String,
 *     description: String,
 *     members: [String, ...],
 *     subscribed: [String, ..],
 *     created_at: Number,
 *     creator: String,
 *     uri: String,
 * }
 */
Cards = new Meteor.Collection("cards");

Cards.allow({
    insert: function (userId, card) {
        /* use createCard instead */
        return false;
    },
    update: function (userId, cards, fields, modifier) {
        return _.all(cards, function (card) {

            // Find the list containing this card
            var list = Lists.findOne({cards: card._id});
            if (!list) {
                return false;
            }

            // For now, all members can edit cards
            var board = Boards.findOne(
                {$and: [
                    {lists: list._id},
                    {members: userId},
                ]});

            if (!board) {
                return false;
            }

            // check name is valid
            if (!(typeof list.name === "string" && list.name.length > 3
                && list.name.length < 140)) {
                return false;
            }

            // check all members exist and are valid
            for (var member_id in card.members) {
                if (board.members.indexOf(member_id) == -1) {
                    return false;
                }
            }
            // same for suscribed members
            for (var member_id in card.members) {
                if (board.members.indexOf(member_id) == -1) {
                    return false;
                }
            }

            var allowed = ["name", "description", "members", "suscribed", ];
            if (_.difference(fields, allowed).length) {
                return false; // tried to write to forbidden field
            }

            // TODO: A good improvement would be to validate the type of the new
            // value of the field (and if a string, the length.) In the
            // future Meteor will have a schema system to makes that easier.
                return true;
        });
    },
    remove: function (userId, cards) {
        return ! _.any(cards, function (card) {
            // Find the list containing this card
            var list = Lists.findOne({cards: card._id});
            if (!list) {
                return false;
            }

            var board = Boards.findOne({lists: list._id});
            return board && board.owner == userId;
        });
    }
});

/// User email
var userEmail = function (user)
{
    if (user.emails && user.emails.length)
        return user.emails[0].address;

    if (user.services && user.services.facebook && user.services.facebook.email)
        return user.services.facebook.email;

    if (user.services && user.services.google && user.services.google.email)
        return user.services.google.email;

    return null;
};
<commit_msg>fixing minor bugs in models<commit_after>// Utils

function randomId()
{
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for(var i = 0; i < 15; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return text;
}

/**
 * Boards
 *
 * Format:
 * {
 *     name: String,
 *     description: String,
 *     lists: [String, ...],
 *
 *     owner: String,
 *     admins: [String, ...],
 *     members: [String, ...],
 *     invited: [String, ...],
 *
 *     created_at: Number,
 *     creator: String,
 *
 *     is_public: Boolean,
 *     labels: [String, ...],
 *     uri: String,
 * }
 */
Boards = new Meteor.Collection("boards");

Boards.allow({
  insert: function (userId, board) {
    return false; // no cowboy inserts -- use createBoard method
  },
  update: function (userId, boards, fields, modifier) {
    return _.all(boards, function (board) {
      if (userId !== board.owner && board.admins.indexOf(userId) == -1) {
        return false; // not the owner
      }

      var allowed = ["name", "description", "lists", ];
      if (_.difference(fields, allowed).length)
        return false; // tried to write to forbidden field

      // TODO: A good improvement would be to validate the type of the new
      // value of the field (and if a string, the length.) In the
      // future Meteor will have a schema system to makes that easier.
      return true;
    });
  },
  remove: function (userId, boards) {
    return ! _.any(boards, function (board) {
      // deny if not the owner
      return board.owner !== userId;
    });
  }
});

Meteor.methods({
    /**
     * Creates a new board, with three empty lists by default:
     * Todo, Doing & Done
     */
    createBoard: function (options)
    {
        if (!this.userId) {
            throw new Meteor.Error(400, "Anonymous user");
        }
        options = options || {};
        if (!(typeof options.name === "string" && options.name.length > 3 &&
            options.name.length < 140))
        {
            throw new Meteor.Error(400, "Invalid name");
        }

        var created_at = new Date().getTime();

        var uri = randomId();

        var todoListId = Lists.insert({
            board_uri: uri,
            name: "Todo",
            cards: [],
            created_at: created_at,
            creator: this.userId
        });

        var doingListId = Lists.insert({
            board_uri: uri,
            name: "Doing",
            cards: [],
            created_at: created_at,
            creator: this.userId
        });

        var doneListId = Lists.insert({
            board_uri: uri,
            name: "Done",
            cards: [],
            created_at: created_at,
            creator: this.userId
        });

        var boardId = Boards.insert({
            name: options.name,
            description: "",
            lists: [todoListId, doingListId, doneListId],
            owner: this.userId,
            admins: [this.userId],
            members: [this.userId],
            invited: [],
            created_at: created_at,
            creator: this.userId,
            is_public: true,
            labels: [],
            uri: uri
        });

        return uri;
    },

    /**
     * Creates a list in a board if you have board edit perms
     */
    createList: function (options) {
        var board = Boards.findOne(
            {$and: [
                {uri: options.board_uri},
                {members: this.userId},
            ]});

        if (!board) {
            throw new Meteor.Error(400, "Invalid permissions");
        }

        if (!(typeof options.name === "string" && options.name.length > 0
            && options.name.length < 140))
        {
            throw new Meteor.Error(400, "Invalid name");
        }

        var listId = Lists.insert({
            board_uri: options.board_uri,
            name: options.name,
            cards: [],
            created_at: new Date().getTime(),
            creator: this.userId,
        });

        Boards.update({uri: options.board_uri}, {$addToSet: {lists: listId}});
        return listId;
    },

    /**
     * Removes a list and all its cards, if you have edit permissions in the
     * board
     */
    removeList: function (options) {
        var list = Lists.findOne({_id: options.list_id});

        if (!list) {
            throw new Meteor.Error(404, "List not found");
        }

        var board = Boards.findOne(
            {$and: [
                {uri: list.board_uri},
                {members: this.userId},
            ]});

        if (!board) {
            throw new Meteor.Error(400, "Invalid permissions");
        }

        Cards.remove({board_uri: board.uri, _id: {$in: list.cards}});
        Lists.remove({_id: list._id});
        Boards.update({uri: board.uri}, {$pull: {$set: {lists: options.list_id}}});
    },

    /**
     * Creates a card in a board list, if you have board edit perms
     */
    createCard: function (list_id, name) {
        var board = Boards.findOne(
            {$and: [
                {lists: list_id},
                {members: this.userId},
            ]});

        if (!board) {
            throw new Meteor.Error(400, "Invalid permissions");
        }

        if (!(typeof name === "string" && name.length > 0 && name.length < 140)) {
            throw new Meteor.Error(400, "Invalid name");
        }

        var uri = randomId();

        var cardId = Cards.insert({
            board_uri: board.uri,
            name: name,
            description: "",
            members: [],
            subscribed: [],
            created_at: new Date().getTime(),
            creator: this.userId,
            uri: uri,
        });

        Lists.update({_id: list_id}, {$addToSet: {cards: cardId}});
        return uri;
    },
});

/**
 * Lists
 *
 * Format:
 * {
 *     board_uri: String,
 *     name: String,
 *     cards: [String, ...],
 *     created_at: Number,
 *     creator: String,
 * }
 */
Lists = new Meteor.Collection("lists");

Lists.allow({
    insert: function (userId, list) {
        /* use createList instead */
        return false;
    },
    update: function (userId, lists, fields, modifier) {
        return _.all(lists, function (list) {

            // For now, all members can edit lists
            var board = Boards.findOne(
                {$and: [
                    {lists: list._id},
                    {members: userId},
                ]});

            // no permissions in this board or board for this list doesn't exist
            if (!board) {
                return false;
            }

            // check all cards exist
            for (var card_id in board.cards) {
                if (Cards.find({_id: card_id}).count() != 1) {
                    return false;
                }
            }

            // check name is valid
            if (!(typeof list.name === "string" && list.name.length > 3
                && list.name.length < 140)) {
                return false;
            }

            var allowed = ["name", "cards", ];
            if (_.difference(fields, allowed).length) {
                return false; // tried to write to forbidden field
            }

            // TODO: A good improvement would be to validate the type of the new
            // value of the field (and if a string, the length.) In the
            // future Meteor will have a schema system to makes that easier.
                return true;
        });
    },
    remove: function (userId, lists) {
        // use removeList instead
        return false;
    }
});

/**
 * Cards
 *
 * Format:
 * {
 *     board_uri: String,
 *     name: String,
 *     description: String,
 *     members: [String, ...],
 *     subscribed: [String, ..],
 *     created_at: Number,
 *     creator: String,
 *     uri: String,
 * }
 */
Cards = new Meteor.Collection("cards");

Cards.allow({
    insert: function (userId, card) {
        /* use createCard instead */
        return false;
    },
    update: function (userId, cards, fields, modifier) {
        return _.all(cards, function (card) {

            // Find the list containing this card
            var list = Lists.findOne({cards: card._id});
            if (!list) {
                return false;
            }

            // For now, all members can edit cards
            var board = Boards.findOne(
                {$and: [
                    {lists: list._id},
                    {members: userId},
                ]});

            if (!board) {
                return false;
            }

            // check name is valid
            if (!(typeof list.name === "string" && list.name.length > 3
                && list.name.length < 140)) {
                return false;
            }

            // check all members exist and are valid
            for (var member_id in card.members) {
                if (board.members.indexOf(member_id) == -1) {
                    return false;
                }
            }
            // same for suscribed members
            for (var member_id in card.members) {
                if (board.members.indexOf(member_id) == -1) {
                    return false;
                }
            }

            var allowed = ["name", "description", "members", "suscribed", ];
            if (_.difference(fields, allowed).length) {
                return false; // tried to write to forbidden field
            }

            // TODO: A good improvement would be to validate the type of the new
            // value of the field (and if a string, the length.) In the
            // future Meteor will have a schema system to makes that easier.
                return true;
        });
    },
    remove: function (userId, cards) {
        return ! _.any(cards, function (card) {
            // Find the list containing this card
            var list = Lists.findOne({cards: card._id});
            if (!list) {
                return false;
            }

            var board = Boards.findOne({lists: list._id});
            return board && board.owner == userId;
        });
    }
});

/// User email
var userEmail = function (user)
{
    if (user.emails && user.emails.length)
        return user.emails[0].address;

    if (user.services && user.services.facebook && user.services.facebook.email)
        return user.services.facebook.email;

    if (user.services && user.services.google && user.services.google.email)
        return user.services.google.email;

    return null;
};
<|endoftext|>


<commit_before>var totalScaleFactor = (1.5*7.91) + (7.97/1.5);
var addScaleFactor = (1.5)/(totalScaleFactor);
var bossScaleFactor = 1/(1.5*totalScaleFactor);

module.exports.models = [
        {"dispName": "Universal Sim", "name": "universal",
        "model": {
            patchwerk_ba_2t: 0.025/2,
            patchwerk_ba_st: 0.07/2,
            patchwerk_sa_2t: 0.02/2,
            patchwerk_sa_st: 0.07/2,
            patchwerk_na_st: 0.07/2,
            lightmovement_ba_2t: 0.155/2,
            lightmovement_ba_st: 0.195/2,
            lightmovement_sa_2t: 0.05/2,
            lightmovement_sa_st: 0.12/2,
            lightmovement_na_2t: 0.03/2,
            lightmovement_na_st: 0.065/2,
            heavymovement_ba_st: 0.08/2,
            heavymovement_sa_2t: 0.015/2,
            heavymovement_sa_st: 0.035/2,
            '30_lightmovement_4_adds': 0.1*addScaleFactor/2,
            '30_lightmovement_5_adds': 0.06*addScaleFactor/2,
            '50_lightmovement_3_adds': 0.15*addScaleFactor/2,
            '50_lightmovement_4_adds': 0.22*addScaleFactor/2,
            '50_lightmovement_5_adds': 0.22*addScaleFactor/2,
            '55_lightmovement_3_adds': 0.15*addScaleFactor/2,
            '55_lightmovement_4_adds': 0.3*addScaleFactor/2,
            '55_lightmovement_5_adds': 0.29*addScaleFactor/2,
            '60_lightmovement_3_adds': 0.14*addScaleFactor/2,
            '60_lightmovement_4_adds': 0.23*addScaleFactor/2,
            '60_lightmovement_5_adds': 0.12*addScaleFactor/2,
            '30_patchwerk_4_adds': 0.3*addScaleFactor/2,
            '30_patchwerk_5_adds': 0.19*addScaleFactor/2,
            '50_patchwerk_3_adds': 0.45*addScaleFactor/2,
            '50_patchwerk_4_adds': 0.66*addScaleFactor/2,
            '50_patchwerk_5_adds': 0.66*addScaleFactor/2,
            '55_patchwerk_3_adds': 0.45*addScaleFactor/2,
            '55_patchwerk_4_adds': 0.9*addScaleFactor/2,
            '55_patchwerk_5_adds': 0.86*addScaleFactor/2,
            '60_patchwerk_3_adds': 0.41*addScaleFactor/2,
            '60_patchwerk_4_adds': 0.69*addScaleFactor/2,
            '60_patchwerk_5_adds': 0.36*addScaleFactor/2,
            patchwerk_ba_st: 0.33*bossScaleFactor/2,
            patchwerk_na_st: 1.43*bossScaleFactor/2,
            lightmovement_ba_st: 1.86*bossScaleFactor/2,
            lightmovement_na_2t: 0.58*bossScaleFactor/2,
            lightmovement_na_st: 1.2*bossScaleFactor/2,
            heavymovement_ba_st: 0.2*bossScaleFactor/2,
            heavymovement_na_2t: 0.2*bossScaleFactor/2,
            heavymovement_na_st: 2.17*bossScaleFactor/2
        },
        "timeModel": {
            90: 1/2,
            250: .15/2,
            400: .85/2
        }
    },
    {"dispName": "Nighthold Composite", "name": "nh",
        "model": {
            patchwerk_ba_2t: 0.025,
            patchwerk_ba_st: 0.07,
            patchwerk_sa_2t: 0.02,
            patchwerk_sa_st: 0.07,
            patchwerk_na_st: 0.07,
            lightmovement_ba_2t: 0.155,
            lightmovement_ba_st: 0.195,
            lightmovement_sa_2t: 0.05,
            lightmovement_sa_st: 0.12,
            lightmovement_na_2t: 0.03,
            lightmovement_na_st: 0.065,
            heavymovement_ba_st: 0.08,
            heavymovement_sa_2t: 0.015,
            heavymovement_sa_st: 0.035
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Tomb of Sargeras Composite", "name": "tos",
        "model": {
            "patchwerk_sa_st": 0.06875,
            "patchwerk_na_st": 0.125,
            "lightmovement_ba_st": 0.05,
            "lightmovement_sa_st": 0.1375,
            "lightmovement_sa_2t": 0.06875,
            "lightmovement_na_st": 0.3,
            "lightmovement_na_2t": 0.05,
            "heavymovement_na_st": 0.14375,
            "heavymovement_na_2t": 0.01875,
            "heavymovement_sa_st": 0.0375
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Mythic+ Composite", "name": "mplus",
        "model": {
            '30_lightmovement_4_adds': 0.1*addScaleFactor,
            '30_lightmovement_5_adds': 0.06*addScaleFactor,
            '50_lightmovement_3_adds': 0.15*addScaleFactor,
            '50_lightmovement_4_adds': 0.22*addScaleFactor,
            '50_lightmovement_5_adds': 0.22*addScaleFactor,
            '55_lightmovement_3_adds': 0.15*addScaleFactor,
            '55_lightmovement_4_adds': 0.3*addScaleFactor,
            '55_lightmovement_5_adds': 0.29*addScaleFactor,
            '60_lightmovement_3_adds': 0.14*addScaleFactor,
            '60_lightmovement_4_adds': 0.23*addScaleFactor,
            '60_lightmovement_5_adds': 0.12*addScaleFactor,
            '30_patchwerk_4_adds': 0.3*addScaleFactor,
            '30_patchwerk_5_adds': 0.19*addScaleFactor,
            '50_patchwerk_3_adds': 0.45*addScaleFactor,
            '50_patchwerk_4_adds': 0.66*addScaleFactor,
            '50_patchwerk_5_adds': 0.66*addScaleFactor,
            '55_patchwerk_3_adds': 0.45*addScaleFactor,
            '55_patchwerk_4_adds': 0.9*addScaleFactor,
            '55_patchwerk_5_adds': 0.86*addScaleFactor,
            '60_patchwerk_3_adds': 0.41*addScaleFactor,
            '60_patchwerk_4_adds': 0.69*addScaleFactor,
            '60_patchwerk_5_adds': 0.36*addScaleFactor,
            patchwerk_ba_st: 0.33*bossScaleFactor,
            patchwerk_na_st: 1.43*bossScaleFactor,
            lightmovement_ba_st: 1.86*bossScaleFactor,
            lightmovement_na_2t: 0.58*bossScaleFactor,
            lightmovement_na_st: 1.2*bossScaleFactor,
            heavymovement_ba_st: 0.2*bossScaleFactor,
            heavymovement_na_2t: 0.2*bossScaleFactor,
            heavymovement_na_st: 2.17*bossScaleFactor
        },
        "timeModel": {
            400: .85
        }
    },
    {   "dispName": "Skorpyron", "name": "skorpyron",
        "model": {
            patchwerk_sa_2t: 0.1,
            patchwerk_sa_st: 0.1,
            lightmovement_sa_st: 0.55,
            lightmovement_na_2t: 0.25
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Chronomatic Anomaly", "name": "anomaly",
        "model": {
            lightmovement_ba_st: 0.2,
            heavymovement_ba_st: 0.8
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Trilliax", "name": "trilliax",
        "model": {
            patchwerk_ba_st: 0.3,
            lightmovement_ba_st: 0.7
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Spellblade Aluriel", "name": "aluriel",
        "model": {
            patchwerk_ba_2t: 0.1,
            patchwerk_ba_st: 0.25,
            lightmovement_ba_2t: 0.65
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Tichondrius", "name": "tich",
        "model": {
            lightmovement_sa_st: 0.65,
            heavymovement_sa_st: 0.35
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "High Botanist Tel'arn", "name": "botanist",
        "model": {
            lightmovement_ba_2t: 0.6,
            lightmovement_sa_2t: 0.25,
            heavymovement_sa_2t: 0.15
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Star Augur Etraeus", "name": "augur",
        "model": {
            lightmovement_ba_st: 0.35,
            lightmovement_na_st: 0.65
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Krosus", "name": "krosus",
        "model": {
            patchwerk_sa_st: 0.4,
            patchwerk_na_st: 0.6,
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Grand Magistrix Elisande", "name": "elisande",
        "model": {
            lightmovement_ba_2t: 0.3,
            lightmovement_ba_st: 0.6,
            lightmovement_na_2t: 0.1
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Gul'dan", "name": "guldan",
        "model": {
            patchwerk_ba_2t: 0.15,
            patchwerk_ba_st: 0.15,
            patchwerk_sa_2t: 0.1,
            patchwerk_sa_st: 0.2,
            patchwerk_na_st: 0.1,
            lightmovement_ba_st: 0.1,
            lightmovement_na_2t: 0.2
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    }
]<commit_msg>Update the models<commit_after>var totalScaleFactor = (1.5*7.91) + (7.97/1.5);
var addScaleFactor = (1.5)/(totalScaleFactor);
var bossScaleFactor = 1/(1.5*totalScaleFactor);

module.exports.models = [
    {"dispName": "Tomb of Sargeras Composite", "name": "tos",
        "model": {
            "patchwerk_sa_st": 0.06875,
            "patchwerk_na_st": 0.125,
            "lightmovement_ba_st": 0.05,
            "lightmovement_sa_st": 0.1375,
            "lightmovement_sa_2t": 0.06875,
            "lightmovement_na_st": 0.3,
            "lightmovement_na_2t": 0.05,
            "heavymovement_na_st": 0.14375,
            "heavymovement_na_2t": 0.01875,
            "heavymovement_sa_st": 0.0375
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
        {"dispName": "Nighthold Composite", "name": "nh",
        "model": {
            patchwerk_ba_2t: 0.025,
            patchwerk_ba_st: 0.07,
            patchwerk_sa_2t: 0.02,
            patchwerk_sa_st: 0.07,
            patchwerk_na_st: 0.07,
            lightmovement_ba_2t: 0.155,
            lightmovement_ba_st: 0.195,
            lightmovement_sa_2t: 0.05,
            lightmovement_sa_st: 0.12,
            lightmovement_na_2t: 0.03,
            lightmovement_na_st: 0.065,
            heavymovement_ba_st: 0.08,
            heavymovement_sa_2t: 0.015,
            heavymovement_sa_st: 0.035
        },
        "timeModel": {
            250: .15,
            400: .85
        }
    },
    {"dispName": "Mythic+ Composite", "name": "mplus",
        "model": {
            '30_lightmovement_4_adds': 0.1*addScaleFactor,
            '30_lightmovement_5_adds': 0.06*addScaleFactor,
            '50_lightmovement_3_adds': 0.15*addScaleFactor,
            '50_lightmovement_4_adds': 0.22*addScaleFactor,
            '50_lightmovement_5_adds': 0.22*addScaleFactor,
            '55_lightmovement_3_adds': 0.15*addScaleFactor,
            '55_lightmovement_4_adds': 0.3*addScaleFactor,
            '55_lightmovement_5_adds': 0.29*addScaleFactor,
            '60_lightmovement_3_adds': 0.14*addScaleFactor,
            '60_lightmovement_4_adds': 0.23*addScaleFactor,
            '60_lightmovement_5_adds': 0.12*addScaleFactor,
            '30_patchwerk_4_adds': 0.3*addScaleFactor,
            '30_patchwerk_5_adds': 0.19*addScaleFactor,
            '50_patchwerk_3_adds': 0.45*addScaleFactor,
            '50_patchwerk_4_adds': 0.66*addScaleFactor,
            '50_patchwerk_5_adds': 0.66*addScaleFactor,
            '55_patchwerk_3_adds': 0.45*addScaleFactor,
            '55_patchwerk_4_adds': 0.9*addScaleFactor,
            '55_patchwerk_5_adds': 0.86*addScaleFactor,
            '60_patchwerk_3_adds': 0.41*addScaleFactor,
            '60_patchwerk_4_adds': 0.69*addScaleFactor,
            '60_patchwerk_5_adds': 0.36*addScaleFactor,
            patchwerk_ba_st: 0.33*bossScaleFactor,
            patchwerk_na_st: 1.43*bossScaleFactor,
            lightmovement_ba_st: 1.86*bossScaleFactor,
            lightmovement_na_2t: 0.58*bossScaleFactor,
            lightmovement_na_st: 1.2*bossScaleFactor,
            heavymovement_ba_st: 0.2*bossScaleFactor,
            heavymovement_na_2t: 0.2*bossScaleFactor,
            heavymovement_na_st: 2.17*bossScaleFactor
        },
        "timeModel": {
            400: .85
        }
    },
    {"dispName": "Goroth", "name": "goroth",
        "model": {
            "patchwerk_na_st": 0.2,
            "lightmovement_na_st": 0.6,
            "heavymovement_na_st": 0.2
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Demonic Inquisition", "name": "inquisition",
        "model": {
            "lightmovement_sa_2t": 0.55,
            "lightmovement_na_2t": 0.35,
            "heavymovement_na_st": 0.15
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Harjatan", "name": "harjatan",
        "model": {
            "patchwerk_sa_st": 0.2,
            "patchwerk_na_st": 0.1,
            "lightmovement_ba_st": 0.2,
            "lightmovement_sa_st": 0.3,
            "heavymovement_sa_st": 0.2
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Mistress Sassz'ine", "name": "mistress",
        "model": {
            "patchwerk_sa_st": 0.15,
            "lightmovement_sa_st": 0.3,
            "lightmovement_na_st": 0.35,
            "heavymovement_na_st": 0.2
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Desolate Host", "name": "host",
        "model": {
            "patchwerk_sa_st": 0.2,
            "lightmovement_sa_st": 0.5,
            "lightmovement_na_st": 0.2,
            "heavymovement_sa_st": 0.1
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Maiden of Vigilance", "name": "maiden",
        "model": {
            "patchwerk_na_st": 0.5,
            "lightmovement_na_st": 0.3,
            "heavymovement_na_st": 0.2
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Sisters of the Moon", "name": "sisters",
        "model": {
            "patchwerk_na_st": 0.1,
            "lightmovement_ba_st": 0.2,
            "lightmovement_na_st": 0.45,
            "heavymovement_na_st": 0.25
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    },
    {"dispName": "Fallen Avatar", "name": "avatar",
        "model": {
            "patchwerk_na_st": 0.1,
            "lightmovement_na_st": 0.5,
            "heavymovement_na_st": 0.2
        },
        "timeModel": {
            "250": 0.15,
            "400": 0.85
        }
    }
]<|endoftext|>


<commit_before>describe("build", function() {
  var build = require("../index");
  var fs = require("fs-extra");

  global.test.server(function(server) {
    // Only server an image at this route if
    // the request passes the correct query
    server.get("/a.jpg", function(req, res) {
      if (!req.query || req.query.user !== "foo" || req.query.pass !== "bar")
        return res.sendStatus(400);

      res.sendFile(__dirname + "/small.jpg");
    });
  });

  global.test.blog();

  it("resolves relative paths inside files", function(done) {
    var path = "/blog/foo.txt";
    var contents = "![Image](_foo.jpg)";
    var absolutePathToImage = "/blog/_foo.jpg";

    fs.outputFileSync(this.blogDirectory + path, contents);

    build(this.blog, path, {}, function(err, entry) {
      if (err) return done.fail(err);

      // It won't be cached since the image doesn't exist
      expect(entry.html).toContain(absolutePathToImage);
      done();
    });
  });

  it("handles image URLs with query strings", function(done) {
    var path = "/hello.txt";
    var contents = "![Image](" + this.origin + "/a.jpg?user=foo&pass=bar)";

    fs.outputFileSync(this.blogDirectory + path, contents);

    build(this.blog, path, {}, function(err, entry) {
      if (err) return done.fail(err);

      // verify the image was cached
      expect(entry.html).toContain("/_image_cache/");

      // verify a thumbnail was generated from the image
      expect(entry.thumbnail.small).toEqual(
        jasmine.objectContaining({
          name: "small.jpg"
        })
      );
      done();
    });
  });

  it("handles images with accents and spaces in their filename", function(done) {
    var path = "/blog/Hello world.txt";
    var contents = "![Best Image Ever](óå g.jpg)";
    var pathToImage = "/blog/óå g.jpg";

    fs.outputFileSync(this.blogDirectory + path, contents);
    fs.copySync(__dirname + "/small.jpg", this.blogDirectory + pathToImage);

    build(this.blog, path, {}, function(err, entry) {
      if (err) return done.fail(err);

      // verify the image was cached
      expect(entry.html).toContain("/_image_cache/");

      // verify a thumbnail was generated from the image
      expect(entry.thumbnail.small).toEqual(
        jasmine.objectContaining({
          name: "small.jpg"
        })
      );

      done();
    });
  });
});
<commit_msg>Updated test to fix pandoc weirdness<commit_after>describe("build", function() {
  var build = require("../index");
  var fs = require("fs-extra");

  global.test.server(function(server) {
    // Only server an image at this route if
    // the request passes the correct query
    server.get("/a.jpg", function(req, res) {
      if (!req.query || req.query.user !== "foo" || req.query.pass !== "bar")
        return res.sendStatus(400);

      res.sendFile(__dirname + "/small.jpg");
    });
  });

  global.test.blog();

  it("resolves relative paths inside files", function(done) {
    var path = "/blog/foo.txt";
    var contents = "![Image](_foo.jpg)";
    var absolutePathToImage = "/blog/_foo.jpg";

    fs.outputFileSync(this.blogDirectory + path, contents);

    build(this.blog, path, {}, function(err, entry) {
      if (err) return done.fail(err);

      // It won't be cached since the image doesn't exist
      expect(entry.html).toContain(absolutePathToImage);
      done();
    });
  });

  fit("handles image URLs with query strings", function(done) {
    var path = "/hello.txt";
    var contents = "Thumbnail: " + this.origin + "/a.jpg?user=foo&pass=bar";

    fs.outputFileSync(this.blogDirectory + path, contents);

    build(this.blog, path, {}, function(err, entry) {
      if (err) return done.fail(err);

      // verify a thumbnail was generated from the image
      expect(entry.thumbnail.small).toEqual(
        jasmine.objectContaining({
          name: "small.jpg"
        })
      );
      done();
    });
  });

  it("handles images with accents and spaces in their filename", function(done) {
    var path = "/blog/Hello world.txt";
    var contents = "![Best Image Ever](óå g.jpg)";
    var pathToImage = "/blog/óå g.jpg";

    fs.outputFileSync(this.blogDirectory + path, contents);
    fs.copySync(__dirname + "/small.jpg", this.blogDirectory + pathToImage);

    build(this.blog, path, {}, function(err, entry) {
      if (err) return done.fail(err);

      // verify the image was cached
      expect(entry.html).toContain("/_image_cache/");

      // verify a thumbnail was generated from the image
      expect(entry.thumbnail.small).toEqual(
        jasmine.objectContaining({
          name: "small.jpg"
        })
      );

      done();
    });
  });
});
<|endoftext|>


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

    angular
        .module('swoleciety.calendar')
        .controller('CalendarController',CalendarController);

    CalendarController.$inject = ['$firebaseArray',
                                  '$firebaseObject',
                                  'firebaseUserExerciseService',
                                  'firebaseUserService',
                                  'FIREBASE_URL',
                                  '$uibModal'];

    function CalendarController($firebaseArray,
                                 $firebaseObject,
                                 firebaseUserExerciseService,
                                 firebaseUserService,
                                 FIREBASE_URL,
                                 $uibModal) {
        var vm = this;
        vm.days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
        vm.weekDates = [];
        vm.weeklyExercises = {};
        angular.forEach(vm.days, function(day) {
            vm.weeklyExercises[day] = [];
        });
        vm.singleDayExercises = {};
        vm.selectedDay = "";

        vm.getWeek = function(today) {
            var today = today || new Date();
            var day = today.getDay();
            var date = today.getDate() - day;
            var startDate = new Date(today.setDate(date));
            var endDate = new Date(today.setDate(date + 6));
            return startDate.toLocaleDateString().replace(/\//g,'-');
        }

        vm.updateWeekDates = function(start) {
            var startDate =  new Date(vm.getWeek());
            vm.weekDates = vm.days.map(function(value, index) {
                var flag = 1;
                if (index == 0) {
                    flag = 0;
                }
                return {
                    day: value,
                    date: new Date(startDate.setDate(startDate.getDate() + flag))
                }
            });
        }

        vm.loadExercisesForWeek = function(week) {
            var currentWeek = week || vm.getWeek();
            if (currentWeek == week) {
                vm.weeklyExercises = {};
                angular.forEach(vm.days, function(day) {
                    vm.weeklyExercises[day] = [];
                });
            }
            var exercises = $firebaseArray(firebaseUserExerciseService.getUserExercises('smistry',currentWeek));
            exercises.$loaded()
            .then(function() {

                angular.forEach(exercises, function(exercise) {
                    angular.forEach(exercise, function(e) {
                        if (e instanceof Object) {
                            console.log(e);
                            vm.weeklyExercises[e.day].push(e);
                        }
                    });

                });
            });
            vm.updateWeekDates();

        };

        vm.openCalendarEditModal = function(exercise,day) {
            $uibModal.open({
                animation: true,
                templateUrl: 'calendar/calendarEditModal.html',
                controller: 'CalendarEditModalController',
                controllerAs: 'vm',
                size: 'md',
                resolve: {
                    exerciseDetails: function() {
                        return exercise;
                    }
                }

            });
        };

        vm.deleteExercise = function(exercise) {
            firebaseUserExerciseService.deleteExercise(exercise);

        };

        vm.loadSingleDay = function(day) {
            var currentWeek = vm.getWeek();
            var exercises = $firebaseArray(firebaseUserExerciseService.getUserExercises('smistry',currentWeek));
            exercises.$loaded()
            .then(function() {
                vm.singleDayExercises[day] = [];
                angular.forEach(exercises, function(exercise) {
                    if (exercise.day == day) {
                        vm.singleDayExercises[day].push(exercise);
                    }
                });
            });

        };

        vm.getPreviousWeek = function() {
            var currentStartOfWeek = new Date(vm.getWeek());
            var lastWeek = vm.getWeek(new Date(currentStartOfWeek.setDate(currentStartOfWeek.getDate() - 7)));
            vm.updateWeekDates(new Date(lastWeek));
            vm.loadExercisesForWeek(lastWeek);
        }

        vm.getNextWeek = function() {
            var currentStartOfWeek = new Date(vm.getWeek());
            var nextWeek = vm.getWeek(new Date(currentStartOfWeek.setDate(currentStartOfWeek.getDate() + 7)));
            vm.updateWeekDates(new Date(nextWeek));
            vm.loadExercisesForWeek(nextWeek);
        }

    }

})();<commit_msg>forward arrow works<commit_after>(function() {
    'use strict';

    angular
        .module('swoleciety.calendar')
        .controller('CalendarController',CalendarController);

    CalendarController.$inject = ['$firebaseArray',
                                  '$firebaseObject',
                                  'firebaseUserExerciseService',
                                  'firebaseUserService',
                                  'FIREBASE_URL',
                                  '$uibModal'];

    function CalendarController($firebaseArray,
                                 $firebaseObject,
                                 firebaseUserExerciseService,
                                 firebaseUserService,
                                 FIREBASE_URL,
                                 $uibModal) {
        var vm = this;
        vm.days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
        vm.weekDates = [];
        vm.weeklyExercises = {};
        angular.forEach(vm.days, function(day) {
            vm.weeklyExercises[day] = [];
        });
        vm.singleDayExercises = {};
        vm.selectedDay = "";
        vm.currentStartOfWeek = "";

        vm.getWeek = function(today) {
            var today = today || new Date();
            var day = today.getDay();
            var date = today.getDate() - day;
            var startDate = new Date(today.setDate(date));
            var endDate = new Date(today.setDate(date + 6));
            return startDate.toLocaleDateString().replace(/\//g,'-');
        }

        vm.updateWeekDates = function(start) {
            var startDate =  start || new Date(vm.getWeek());
            if (startDate == start) {
                console.log('updating week for startdate' + startDate);
            }
            else {
                console.log('updating week for getweek' + vm.getWeek());
            }

            vm.weekDates = vm.days.map(function(value, index) {
                var offset = 1;
                if (index == 0) {
                    offset = 0;
                }
                return {
                    day: value,
                    date: new Date(startDate.setDate(startDate.getDate() + offset))
                }
            });
            vm.currentStartOfWeek = vm.weekDates[0].date;
        }

        vm.loadExercisesForWeek = function(week) {
            var currentWeek = week || vm.getWeek();
            vm.currentStartOfWeek = new Date(currentWeek);
            if (currentWeek == week) {
                console.log('loading exercises for week ' + week);
                vm.weeklyExercises = {};
                angular.forEach(vm.days, function(day) {
                    vm.weeklyExercises[day] = [];
                });
            }
            else{
                console.log('loading exercises for week getweek ' + vm.getWeek());
            }
            var exercises = $firebaseArray(firebaseUserExerciseService.getUserExercises('smistry',currentWeek));
            exercises.$loaded()
            .then(function() {
                console.log(exercises);
                angular.forEach(exercises, function(exercise) {
                    angular.forEach(exercise, function(e) {
                        if (e instanceof Object && e != undefined) {
                            console.log(e);
                            vm.weeklyExercises[e.day].push(e);
                        }
                    });

                });
            });
            vm.updateWeekDates(vm.currentStartOfWeek);

        };

        vm.openCalendarEditModal = function(exercise,day) {
            $uibModal.open({
                animation: true,
                templateUrl: 'calendar/calendarEditModal.html',
                controller: 'CalendarEditModalController',
                controllerAs: 'vm',
                size: 'md',
                resolve: {
                    exerciseDetails: function() {
                        return exercise;
                    }
                }

            });
        };

        vm.deleteExercise = function(exercise) {
            firebaseUserExerciseService.deleteExercise(exercise);

        };

/*
        vm.loadSingleDay = function(day) {
            var currentWeek = vm.getWeek();
            var exercises = $firebaseArray(firebaseUserExerciseService.getUserExercises('smistry',currentWeek));
            exercises.$loaded()
            .then(function() {
                vm.singleDayExercises[day] = [];
                angular.forEach(exercises, function(exercise) {
                    if (exercise.day == day) {
                        vm.singleDayExercises[day].push(exercise);
                    }
                });
            });

        };
     */

        vm.getPreviousWeek = function() {
            var currentStartOfWeek = new Date(vm.getWeek());
            var lastWeek = vm.getWeek(new Date(currentStartOfWeek.setDate(currentStartOfWeek.getDate() - 7)));
            vm.updateWeekDates(new Date(lastWeek));
            vm.loadExercisesForWeek(lastWeek);
        }

        vm.getNextWeek = function() {
            var currentStartOfWeek = vm.currentStartOfWeek;
            var nextWeek = vm.getWeek(new Date(currentStartOfWeek.setDate(currentStartOfWeek.getDate() + 7)));
            vm.loadExercisesForWeek(nextWeek);
        }

    }

})();<|endoftext|>


<commit_before>'use strict';

var modern = (function() {

    var ios5 = (function() {
        return !!(navigator.userAgent.match(/OS 5(_\d)+ like Mac OS X/i));
    }());

    var androidOld = (function() {
        var ua = navigator.userAgent;
        return !!(ua.match(/Android/i) && parseFloat(ua.slice(ua.indexOf('Android')+8)) < 4);
    }());

    var androidStock = (function() {
        var ua = navigator.userAgent;
        var regExAppleWebKit = new RegExp(/AppleWebKit\/([\d.]+)/);
        var resultAppleWebKitRegEx = regExAppleWebKit.exec(ua);
        var appleWebKitVersion = (resultAppleWebKitRegEx === null ? null : parseFloat(regExAppleWebKit.exec(ua)[1]));
        var isAndroidBrowser = ua.match(/Android/i) && appleWebKitVersion !== null && appleWebKitVersion < 537;
        return isAndroidBrowser;
    }());

    var ie9Down = (function() {
        var div = document.createElement('div');
        div.innerHTML = '';
        return (div.getElementsByTagName('i').length === 1);
    }());

    var es5 = (function() {
        try {
            Object.defineProperty({}, 'x', {});
            Object.create({});
        } catch(e) {
            return false;
        }
        return true;
    }());

    var canvas = (function() {
        var el = document.createElement('canvas');
        return !!(el.getContext && el.getContext('2d'));
    }());

    var smallViewport = (function() {
        return Math.max( window.screen.width, window.screen.height, window.outerWidth, window.outerHeight ) <= 480;
    }());

    return !!(canvas && es5 && !(ios5 || androidOld || androidStock || ie9Down || smallViewport));

}());

if(Modernizr) {
    Modernizr.addTest('modern', function() {
        return modern;
    });
}
else if (typeof module === 'object' && module.exports) {
    module.exports = modern;
}
<commit_msg>fixes modern browser test error<commit_after>'use strict';

var modern = (function() {

    var ios5 = (function() {
        return !!(navigator.userAgent.match(/OS 5(_\d)+ like Mac OS X/i));
    }());

    var androidOld = (function() {
        var ua = navigator.userAgent;
        return !!(ua.match(/Android/i) && parseFloat(ua.slice(ua.indexOf('Android')+8)) < 4);
    }());

    var androidStock = (function() {
        var ua = navigator.userAgent;
        var regExAppleWebKit = new RegExp(/AppleWebKit\/([\d.]+)/);
        var resultAppleWebKitRegEx = regExAppleWebKit.exec(ua);
        var appleWebKitVersion = (resultAppleWebKitRegEx === null ? null : parseFloat(regExAppleWebKit.exec(ua)[1]));
        var isAndroidBrowser = ua.match(/Android/i) && appleWebKitVersion !== null && appleWebKitVersion < 537;
        return isAndroidBrowser;
    }());

    var ie9Down = (function() {
        var div = document.createElement('div');
        div.innerHTML = '';
        return (div.getElementsByTagName('i').length === 1);
    }());

    var es5 = (function() {
        try {
            Object.defineProperty({}, 'x', {});
            Object.create({});
        } catch(e) {
            return false;
        }
        return true;
    }());

    var canvas = (function() {
        var el = document.createElement('canvas');
        return !!(el.getContext && el.getContext('2d'));
    }());

    var smallViewport = (function() {
        return Math.max( window.screen.width, window.screen.height, window.outerWidth, window.outerHeight ) <= 480;
    }());

    return !!(canvas && es5 && !(ios5 || androidOld || androidStock || ie9Down || smallViewport));

}());

if(window.Modernizr) {
    window.Modernizr.addTest('modern', function() {
        return modern;
    });
}
else if (typeof module === 'object' && module.exports) {
    module.exports = modern;
}
<|endoftext|>