<commit_before>module.exports = function(grunt) {
    var fs=require("fs");
    var pathModule=require("path");

    var filterTypeScriptFiles = function(content, srcPath) {
        var matchRule = /^.*@private/ig;
        if (matchRule.test(content))
        {
            return false;
        }
        var processed = content;
        processed = processed.replace(/\/\/[\/\s]*@private((.|\s)*?)\/\/[\/\s]*@endprivate/gm, "");
        return processed;
    };

    var updatePackageDef = function(content, update)
    {
        var contentAsObject = JSON.parse(content);
        update(contentAsObject);
        return JSON.stringify(contentAsObject, null, "\t");
    }

    var updateModulesPackageDef = function(content, srcPath) {
        return updatePackageDef(content, function(contentAsObject) {
            contentAsObject.version = localCfg.packageVersion;
            if (localCfg.commitSHA) {
                contentAsObject.repository.url += "/commit/" + localCfg.commitSHA;
            }
        });
    };

    var updateAppPackageDef = function(content, srcPath) {
        var currentAppName = grunt.task.current.data.appName;
        return updatePackageDef(content, function(contentAsObject) {
            contentAsObject.version = localCfg.packageVersion;
            contentAsObject.name = "tns-samples-" + currentAppName;
            contentAsObject.description = "Nativescript " + currentAppName + " sample application";
            contentAsObject.license = "BSD";
            if (!contentAsObject.repository) {
                contentAsObject.repository = {};
            }
            if (!contentAsObject.repository.url) {
                contentAsObject.repository.url = localCfg.mainPackageContent.repository.url;
            }
            if (localCfg.commitSHA) {
                contentAsObject.repository.url += "/commit/" + localCfg.commitSHA;
            }
        });
    };

    var updateDefinitionsPackageDef = function(content, srcPath) {
        return updatePackageDef(content, function(contentAsObject) {
            contentAsObject.version = localCfg.packageVersion;
            contentAsObject.name = "tns-definitions";
            contentAsObject.description = "NativeScript Module definitions";
            contentAsObject.license = "Apache-2.0";
            if (localCfg.commitSHA) {
                contentAsObject.repository.url += "/commit/" + localCfg.commitSHA;
            }
        });
    };

    var getCommitSha = function() {
        return ""
    };

    var getPackageVersion = function() {
        var buildVersion = process.env.PACKAGE_VERSION;
        if (!buildVersion) {
            return localCfg.mainPackageContent.version;
        }
        return packageContent.version + "-" + buildVersion;
    };

    var processAppFile = function(content, srcPath) {
        return content;
    };

    var getSubDirs = function(dir) {
        var allObjects = fs.readdirSync(dir);
        var allDirs = [];
        for (var i=0; i<allObjects.length; i++)
        {
            var currentObjName = allObjects[i];
            var currentObjPath = pathModule.join(dir, currentObjName);
            var stats = fs.statSync(currentObjPath);
            if (stats.isDirectory())
            {
                allDirs.push({name: currentObjName, path: currentObjPath});
            }
        }
        return allDirs;
    }

    var localCfg = {
        srcDir: ".",
        srcAppsDir: "./apps",
        packageJsonFilePath: "./package.json",
        outDir: "./bin/dist",
        outModulesDir: "./bin/dist/modules",
        outAppsDir: "./bin/dist/apps",
        outTsAppsDir: "./bin/dist/ts-apps",
        outDefinitionsDir: "./bin/dist/definitions",

        excludedModules: [
            "!./ui/slide-out/**/*.*"
        ]
    };
    localCfg.mainPackageContent = grunt.file.readJSON(localCfg.packageJsonFilePath);
    localCfg.packageVersion = getPackageVersion(localCfg.packageJsonFilePath);
    localCfg.commitSHA = getCommitSha();
    localCfg.defaultExcludes = [
            "!" + localCfg.outDir + "/**/*.*",
            "!./node_modules/**/*.*",
            "!./bin/**/*.*",
            "!./build/**/*.*",
            "!./Deploy/**/*.*",
            "!./obj/**/*.*",
            "!./out/**/*.*",
            "!./.*/**/*.*"
        ];
    localCfg.typeScriptSrc = [
            "./**/*.ts"
        ].concat(localCfg.defaultExcludes).concat(localCfg.excludedModules);
    localCfg.typeScriptSrcForTsLint = localCfg.typeScriptSrc.concat([
        "!./ios.d.ts",
        "!./android17.d.ts",
        "!./libjs.d.ts"
    ]);

    grunt.initConfig({
        localCfg : localCfg,
        pkg: grunt.file.readJSON('package.json'),
        clean: {
            build: {
                src: [localCfg.outDir]
            },
            typeScriptLeftovers: {
                expand: true,
                src: [
                    "./.baseDir.*",
                    "./_references.js",
                    "./**/*.map"
                ],
                cwd: localCfg.outModulesDir
            },
            readyAppFiles: {
                src: [localCfg.outModulesDir + "/apps/**"]
            }
        },
        copy: {
            jsLibs: {
                expand: true,
                src: [
                    "./js-libs/**/*.js",
                ],
                dest: "<%= localCfg.outModulesDir %>/",
                cwd: localCfg.srcDir
            },
            license: {
                expand: true,
                src: [
                    "./LICENSE",
                ],
                dest: "<%= localCfg.outModulesDir %>/",
                cwd: localCfg.srcDir
            },
            appLicense: {
                expand: true,
                src: ["./LICENSE"],
                cwd: localCfg.srcAppsDir,
                dest: "__dummy__"
            },
            definitionFiles: {
                src: [
                    localCfg.srcDir + "/**/*.d.ts",
                    pathModule.join(localCfg.srcDir, "LICENSE"),
                    //Exclude the d.ts files in the apps folder - these are part of the apps and are already packed there!
                    "!" + localCfg.srcDir + "/apps/**"
                ].concat(localCfg.defaultExcludes).concat(localCfg.excludedModules),
                dest: localCfg.outDefinitionsDir + "/",
                options: {
                    process: filterTypeScriptFiles
                }
            },
            modulesPackageDef: {
                src: localCfg.packageJsonFilePath,
                dest: localCfg.outModulesDir + "/",
                options: {
                    process: updateModulesPackageDef
                }
            },
            definitionsPackageDef: {
                src: localCfg.packageJsonFilePath,
                dest: localCfg.outDefinitionsDir + "/",
                options: {
                    process: updateDefinitionsPackageDef
                }
            },
            appPackageDef: {
                expand: true,
                flatten: true,
                src: "__app_package_json",
                dest:  "__dummy__",
                options: {
                    process: updateAppPackageDef
                }
            },
            childPackageFiles: {
                src: [
                    localCfg.srcDir + "/**/package.json",
                    "!./package.json",
                    "!./Deploy/**/*.*",
                    "!./bin/**/*.*",
                    "!./Tests/**/*.*",
                    "!" + localCfg.outDir + "/**/*.*"
                ].concat(localCfg.excludedModules),
                dest: localCfg.outModulesDir + "/"
            },
            rawAppsFiles: {
                expand: true,
                src: [
                    "**/*.*",
                    "**/*",
                    "!**/*.map",
                    "!**/*.ts"
                ],
                dest: localCfg.outAppsDir,
                cwd: localCfg.srcAppsDir,
                dot: true
            },
            readyAppFiles: {
                expand: true,
                src: ["./**/*.*"],
                dest: localCfg.outAppsDir + "/",
                cwd: localCfg.outModulesDir + "/apps/",
                options: {
                    process: processAppFile
                }
            },
            readyTsAppFiles: {
                expand: true,
                src: ["./**/*.*", "!./**/*.map"],
                dest: localCfg.outTsAppsDir + "/",
                cwd: localCfg.srcAppsDir
            },
            readyPackages: {
                expand: true,
                src: ["./**/*.tgz"],
                dest: localCfg.outDir + "/",
                cwd: localCfg.outDir,
                flatten: true
            }
        },
        ts: {
            build: {
                src: localCfg.typeScriptSrc,
                outDir: [localCfg.outModulesDir],
                options: {
                    module: "commonjs",
                    target: "es5",
                    sourceMap: false,
                    declaration: false,
                    removeComments: "<%= !grunt.option('leavecomments') || '' %>",
                    compiler: "node_modules/typescript/bin/tsc"
                }
            }
        },
        tslint: {
            build: {
                files: {
                    src: localCfg.typeScriptSrcForTsLint
                },
                options: {
                    configuration: grunt.file.readJSON("./build/tslint.json")
                }
            }
        },
        exec: {
            packModules: {
                cmd: "npm pack",
                cwd: localCfg.outModulesDir + "/"
            },
            packDefinitions: {
                cmd: "npm pack",
                cwd: localCfg.outDefinitionsDir + "/"
            },
            packApp: {
                cmd: "npm pack",
                cwd: "__dummy__"
            }
        },
        multidest: {
            copyLicenseFiles: {
                tasks: ["copy:appLicense"],
                dest: function() {
                    var apps = getSubDirs(localCfg.srcAppsDir);
                    var targetDirs = [];
                    apps.forEach(function(item){
                        targetDirs.push(pathModule.join(localCfg.outAppsDir, item.name));
                        targetDirs.push(pathModule.join(localCfg.outTsAppsDir, item.name));
                    });
                    return targetDirs;
                }()
            }
        }
    });

    grunt.loadNpmTasks("grunt-ts");
    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-exec");
    grunt.loadNpmTasks("grunt-tslint");
    grunt.loadNpmTasks("grunt-multi-dest");

    var cloneTasks = function(originalTasks, taskNameSuffix)
    {
        var clonedTasks = [];
        for(var i=0; i<originalTasks.length; i++)
        {
            var originalTask = originalTasks[i];

            var taskCfg = grunt.util._.clone(grunt.config(originalTask.name));
            var taskName = grunt.util._.clone(originalTask.name);
            taskName[1] = taskName[1] + "_" + taskNameSuffix;

            originalTask.specializeCfg(taskCfg, taskNameSuffix);

            clonedTasks.push({name: taskName, cfg: taskCfg});
        }
        return clonedTasks;
    }

    var enqueueTasks = function(tasks) {
        for (var i=0; i<tasks.length; i++) {
            var task = tasks[i];
            grunt.config(task.name, task.cfg);
            grunt.task.run(task.name.join(":"));
        }
    }

    grunt.registerTask("processEachApp", function(outAppsDir, pkgAppNameSuffix){
        var allapps = getSubDirs(localCfg.srcAppsDir);
        var tasks = [
            {
                name: ["copy", "appPackageDef"],
                specializeCfg: function (cfg, currentAppName) {
                    outAppDir = pathModule.join(outAppsDir, currentAppName);
                    var pkgFilePath = pathModule.join(outAppDir, "package.json");
                    cfg.src = pkgFilePath;
                    cfg.dest = outAppDir;
                    cfg.appName = currentApp.name + (pkgAppNameSuffix || "");
                }
            },
            {
                name: ["exec", "packApp"],
                specializeCfg: function(cfg, currentAppName) {
                    cfg.cwd = pathModule.join(outAppsDir, currentAppName);
                }
            }
        ];

        for (var j=0; j<allapps.length; j++)
        {
            var currentApp = allapps[j];
            var clonedTasks = cloneTasks(tasks, currentApp.name);

            enqueueTasks(clonedTasks);
        }
    });

    grunt.registerTask("tests", [
        "default"
    ]);

    grunt.registerTask("collect-apps-raw-files", [
        "copy:rawAppsFiles",
        "multidest:copyLicenseFiles"
    ]);

    // Does nothing to avoid copying the same files twice. Instead,
    //  the distribute-definition-files tasks copies the d.ts files
    //  from the source root directly to the output folder.
    grunt.registerTask("collect-definitions-raw-files", []);

    grunt.registerTask("collect-modules-raw-files", [
        "copy:jsLibs",
        "copy:license"
    ]);

    grunt.registerTask("compile-ts", [
        "ts:build",
        "clean:typeScriptLeftovers",
        "copy:childPackageFiles"
    ]);
    grunt.registerTask("distribute-apps-files", [
        "copy:readyAppFiles",
        "clean:readyAppFiles"
    ]);
    grunt.registerTask("distribute-ts-apps-files", [
        "copy:readyTsAppFiles"
    ]);
    grunt.registerTask("distribute-definition-files", [
        "copy:definitionFiles"
    ]);
    grunt.registerTask("pack-modules", [
        "copy:modulesPackageDef",
        "exec:packModules"
    ]);
    grunt.registerTask("pack-apps", [
        "processEachApp:" + localCfg.outAppsDir
    ]);
    grunt.registerTask("pack-ts-apps", [
        "processEachApp:" + localCfg.outTsAppsDir + ":-ts"
    ]);
    grunt.registerTask("pack-definitions", [
        "copy:definitionsPackageDef",
        "exec:packDefinitions"
    ]);
    grunt.registerTask("get-ready-packages", [
        "copy:readyPackages"
    ]);

    grunt.registerTask("default", ((typeof(grunt.option('runtslint')) != "undefined" && !grunt.option('runtslint')) ? [] : ["tslint:build"]).concat([
        "clean:build",

        "collect-apps-raw-files",
        "collect-definitions-raw-files",
        "collect-modules-raw-files",

        "compile-ts",
        "distribute-apps-files",
        "distribute-ts-apps-files",
        "distribute-definition-files",

        "pack-modules",
        "pack-apps",
        "pack-ts-apps",
        "pack-definitions",
        "get-ready-packages"
    ]));
};
<commit_msg>Use GIT_COMMIT if found in the environment<commit_after>module.exports = function(grunt) {
    var fs=require("fs");
    var pathModule=require("path");

    var filterTypeScriptFiles = function(content, srcPath) {
        var matchRule = /^.*@private/ig;
        if (matchRule.test(content))
        {
            return false;
        }
        var processed = content;
        processed = processed.replace(/\/\/[\/\s]*@private((.|\s)*?)\/\/[\/\s]*@endprivate/gm, "");
        return processed;
    };

    var updatePackageDef = function(content, update)
    {
        var contentAsObject = JSON.parse(content);
        update(contentAsObject);
        return JSON.stringify(contentAsObject, null, "\t");
    }

    var updateModulesPackageDef = function(content, srcPath) {
        return updatePackageDef(content, function(contentAsObject) {
            contentAsObject.version = localCfg.packageVersion;
            if (localCfg.commitSHA) {
                contentAsObject.repository.url += "/commit/" + localCfg.commitSHA;
            }
        });
    };

    var updateAppPackageDef = function(content, srcPath) {
        var currentAppName = grunt.task.current.data.appName;
        return updatePackageDef(content, function(contentAsObject) {
            contentAsObject.version = localCfg.packageVersion;
            contentAsObject.name = "tns-samples-" + currentAppName;
            contentAsObject.description = "Nativescript " + currentAppName + " sample application";
            contentAsObject.license = "BSD";
            if (!contentAsObject.repository) {
                contentAsObject.repository = {};
            }
            if (!contentAsObject.repository.url) {
                contentAsObject.repository.url = localCfg.mainPackageContent.repository.url;
            }
            if (localCfg.commitSHA) {
                contentAsObject.repository.url += "/commit/" + localCfg.commitSHA;
            }
        });
    };

    var updateDefinitionsPackageDef = function(content, srcPath) {
        return updatePackageDef(content, function(contentAsObject) {
            contentAsObject.version = localCfg.packageVersion;
            contentAsObject.name = "tns-definitions";
            contentAsObject.description = "NativeScript Module definitions";
            contentAsObject.license = "Apache-2.0";
            if (localCfg.commitSHA) {
                contentAsObject.repository.url += "/commit/" + localCfg.commitSHA;
            }
        });
    };

    var getCommitSha = function() {
        if (process.env.GIT_COMMIT) {
            return process.env.GIT_COMMIT;
        return "";
    };

    var getPackageVersion = function() {
        var buildVersion = process.env.PACKAGE_VERSION;
        if (!buildVersion) {
            return localCfg.mainPackageContent.version;
        }
        return packageContent.version + "-" + buildVersion;
    };

    var processAppFile = function(content, srcPath) {
        return content;
    };

    var getSubDirs = function(dir) {
        var allObjects = fs.readdirSync(dir);
        var allDirs = [];
        for (var i=0; i<allObjects.length; i++)
        {
            var currentObjName = allObjects[i];
            var currentObjPath = pathModule.join(dir, currentObjName);
            var stats = fs.statSync(currentObjPath);
            if (stats.isDirectory())
            {
                allDirs.push({name: currentObjName, path: currentObjPath});
            }
        }
        return allDirs;
    }

    var localCfg = {
        srcDir: ".",
        srcAppsDir: "./apps",
        packageJsonFilePath: "./package.json",
        outDir: "./bin/dist",
        outModulesDir: "./bin/dist/modules",
        outAppsDir: "./bin/dist/apps",
        outTsAppsDir: "./bin/dist/ts-apps",
        outDefinitionsDir: "./bin/dist/definitions",

        excludedModules: [
            "!./ui/slide-out/**/*.*"
        ]
    };
    localCfg.mainPackageContent = grunt.file.readJSON(localCfg.packageJsonFilePath);
    localCfg.packageVersion = getPackageVersion(localCfg.packageJsonFilePath);
    localCfg.commitSHA = getCommitSha();
    localCfg.defaultExcludes = [
            "!" + localCfg.outDir + "/**/*.*",
            "!./node_modules/**/*.*",
            "!./bin/**/*.*",
            "!./build/**/*.*",
            "!./Deploy/**/*.*",
            "!./obj/**/*.*",
            "!./out/**/*.*",
            "!./.*/**/*.*"
        ];
    localCfg.typeScriptSrc = [
            "./**/*.ts"
        ].concat(localCfg.defaultExcludes).concat(localCfg.excludedModules);
    localCfg.typeScriptSrcForTsLint = localCfg.typeScriptSrc.concat([
        "!./ios.d.ts",
        "!./android17.d.ts",
        "!./libjs.d.ts"
    ]);

    grunt.initConfig({
        localCfg : localCfg,
        pkg: grunt.file.readJSON('package.json'),
        clean: {
            build: {
                src: [localCfg.outDir]
            },
            typeScriptLeftovers: {
                expand: true,
                src: [
                    "./.baseDir.*",
                    "./_references.js",
                    "./**/*.map"
                ],
                cwd: localCfg.outModulesDir
            },
            readyAppFiles: {
                src: [localCfg.outModulesDir + "/apps/**"]
            }
        },
        copy: {
            jsLibs: {
                expand: true,
                src: [
                    "./js-libs/**/*.js",
                ],
                dest: "<%= localCfg.outModulesDir %>/",
                cwd: localCfg.srcDir
            },
            license: {
                expand: true,
                src: [
                    "./LICENSE",
                ],
                dest: "<%= localCfg.outModulesDir %>/",
                cwd: localCfg.srcDir
            },
            appLicense: {
                expand: true,
                src: ["./LICENSE"],
                cwd: localCfg.srcAppsDir,
                dest: "__dummy__"
            },
            definitionFiles: {
                src: [
                    localCfg.srcDir + "/**/*.d.ts",
                    pathModule.join(localCfg.srcDir, "LICENSE"),
                    //Exclude the d.ts files in the apps folder - these are part of the apps and are already packed there!
                    "!" + localCfg.srcDir + "/apps/**"
                ].concat(localCfg.defaultExcludes).concat(localCfg.excludedModules),
                dest: localCfg.outDefinitionsDir + "/",
                options: {
                    process: filterTypeScriptFiles
                }
            },
            modulesPackageDef: {
                src: localCfg.packageJsonFilePath,
                dest: localCfg.outModulesDir + "/",
                options: {
                    process: updateModulesPackageDef
                }
            },
            definitionsPackageDef: {
                src: localCfg.packageJsonFilePath,
                dest: localCfg.outDefinitionsDir + "/",
                options: {
                    process: updateDefinitionsPackageDef
                }
            },
            appPackageDef: {
                expand: true,
                flatten: true,
                src: "__app_package_json",
                dest:  "__dummy__",
                options: {
                    process: updateAppPackageDef
                }
            },
            childPackageFiles: {
                src: [
                    localCfg.srcDir + "/**/package.json",
                    "!./package.json",
                    "!./Deploy/**/*.*",
                    "!./bin/**/*.*",
                    "!./Tests/**/*.*",
                    "!" + localCfg.outDir + "/**/*.*"
                ].concat(localCfg.excludedModules),
                dest: localCfg.outModulesDir + "/"
            },
            rawAppsFiles: {
                expand: true,
                src: [
                    "**/*.*",
                    "**/*",
                    "!**/*.map",
                    "!**/*.ts"
                ],
                dest: localCfg.outAppsDir,
                cwd: localCfg.srcAppsDir,
                dot: true
            },
            readyAppFiles: {
                expand: true,
                src: ["./**/*.*"],
                dest: localCfg.outAppsDir + "/",
                cwd: localCfg.outModulesDir + "/apps/",
                options: {
                    process: processAppFile
                }
            },
            readyTsAppFiles: {
                expand: true,
                src: ["./**/*.*", "!./**/*.map"],
                dest: localCfg.outTsAppsDir + "/",
                cwd: localCfg.srcAppsDir
            },
            readyPackages: {
                expand: true,
                src: ["./**/*.tgz"],
                dest: localCfg.outDir + "/",
                cwd: localCfg.outDir,
                flatten: true
            }
        },
        ts: {
            build: {
                src: localCfg.typeScriptSrc,
                outDir: [localCfg.outModulesDir],
                options: {
                    module: "commonjs",
                    target: "es5",
                    sourceMap: false,
                    declaration: false,
                    removeComments: "<%= !grunt.option('leavecomments') || '' %>",
                    compiler: "node_modules/typescript/bin/tsc"
                }
            }
        },
        tslint: {
            build: {
                files: {
                    src: localCfg.typeScriptSrcForTsLint
                },
                options: {
                    configuration: grunt.file.readJSON("./build/tslint.json")
                }
            }
        },
        exec: {
            packModules: {
                cmd: "npm pack",
                cwd: localCfg.outModulesDir + "/"
            },
            packDefinitions: {
                cmd: "npm pack",
                cwd: localCfg.outDefinitionsDir + "/"
            },
            packApp: {
                cmd: "npm pack",
                cwd: "__dummy__"
            }
        },
        multidest: {
            copyLicenseFiles: {
                tasks: ["copy:appLicense"],
                dest: function() {
                    var apps = getSubDirs(localCfg.srcAppsDir);
                    var targetDirs = [];
                    apps.forEach(function(item){
                        targetDirs.push(pathModule.join(localCfg.outAppsDir, item.name));
                        targetDirs.push(pathModule.join(localCfg.outTsAppsDir, item.name));
                    });
                    return targetDirs;
                }()
            }
        }
    });

    grunt.loadNpmTasks("grunt-ts");
    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-exec");
    grunt.loadNpmTasks("grunt-tslint");
    grunt.loadNpmTasks("grunt-multi-dest");

    var cloneTasks = function(originalTasks, taskNameSuffix)
    {
        var clonedTasks = [];
        for(var i=0; i<originalTasks.length; i++)
        {
            var originalTask = originalTasks[i];

            var taskCfg = grunt.util._.clone(grunt.config(originalTask.name));
            var taskName = grunt.util._.clone(originalTask.name);
            taskName[1] = taskName[1] + "_" + taskNameSuffix;

            originalTask.specializeCfg(taskCfg, taskNameSuffix);

            clonedTasks.push({name: taskName, cfg: taskCfg});
        }
        return clonedTasks;
    }

    var enqueueTasks = function(tasks) {
        for (var i=0; i<tasks.length; i++) {
            var task = tasks[i];
            grunt.config(task.name, task.cfg);
            grunt.task.run(task.name.join(":"));
        }
    }

    grunt.registerTask("processEachApp", function(outAppsDir, pkgAppNameSuffix){
        var allapps = getSubDirs(localCfg.srcAppsDir);
        var tasks = [
            {
                name: ["copy", "appPackageDef"],
                specializeCfg: function (cfg, currentAppName) {
                    outAppDir = pathModule.join(outAppsDir, currentAppName);
                    var pkgFilePath = pathModule.join(outAppDir, "package.json");
                    cfg.src = pkgFilePath;
                    cfg.dest = outAppDir;
                    cfg.appName = currentApp.name + (pkgAppNameSuffix || "");
                }
            },
            {
                name: ["exec", "packApp"],
                specializeCfg: function(cfg, currentAppName) {
                    cfg.cwd = pathModule.join(outAppsDir, currentAppName);
                }
            }
        ];

        for (var j=0; j<allapps.length; j++)
        {
            var currentApp = allapps[j];
            var clonedTasks = cloneTasks(tasks, currentApp.name);

            enqueueTasks(clonedTasks);
        }
    });

    grunt.registerTask("tests", [
        "default"
    ]);

    grunt.registerTask("collect-apps-raw-files", [
        "copy:rawAppsFiles",
        "multidest:copyLicenseFiles"
    ]);

    // Does nothing to avoid copying the same files twice. Instead,
    //  the distribute-definition-files tasks copies the d.ts files
    //  from the source root directly to the output folder.
    grunt.registerTask("collect-definitions-raw-files", []);

    grunt.registerTask("collect-modules-raw-files", [
        "copy:jsLibs",
        "copy:license"
    ]);

    grunt.registerTask("compile-ts", [
        "ts:build",
        "clean:typeScriptLeftovers",
        "copy:childPackageFiles"
    ]);
    grunt.registerTask("distribute-apps-files", [
        "copy:readyAppFiles",
        "clean:readyAppFiles"
    ]);
    grunt.registerTask("distribute-ts-apps-files", [
        "copy:readyTsAppFiles"
    ]);
    grunt.registerTask("distribute-definition-files", [
        "copy:definitionFiles"
    ]);
    grunt.registerTask("pack-modules", [
        "copy:modulesPackageDef",
        "exec:packModules"
    ]);
    grunt.registerTask("pack-apps", [
        "processEachApp:" + localCfg.outAppsDir
    ]);
    grunt.registerTask("pack-ts-apps", [
        "processEachApp:" + localCfg.outTsAppsDir + ":-ts"
    ]);
    grunt.registerTask("pack-definitions", [
        "copy:definitionsPackageDef",
        "exec:packDefinitions"
    ]);
    grunt.registerTask("get-ready-packages", [
        "copy:readyPackages"
    ]);

    grunt.registerTask("default", ((typeof(grunt.option('runtslint')) != "undefined" && !grunt.option('runtslint')) ? [] : ["tslint:build"]).concat([
        "clean:build",

        "collect-apps-raw-files",
        "collect-definitions-raw-files",
        "collect-modules-raw-files",

        "compile-ts",
        "distribute-apps-files",
        "distribute-ts-apps-files",
        "distribute-definition-files",

        "pack-modules",
        "pack-apps",
        "pack-ts-apps",
        "pack-definitions",
        "get-ready-packages"
    ]));
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),       // Read in package variable from package.json

    // Construct a banner containing package and build information
    banner: '/* <%= pkg.name %> | <%= pkg.url %> | <%= pkg.license %>\n' +
            ' * <%= pkg.author %> | <%= pkg.contact %>\n' +
            ' */\n',

    filename: '<%= pkg.name %>',

    dependencyName: 'angular-package',

    s: 'src/',  // The source directory
    d: 'app/',  // The distributable directory, where built files will end up
    t: 'test/', // The test directory, for unit test files/specs

    angularFiles: [
      '<%=s%>bower_components/angular/angular.js',
      '<%=s%>bower_components/angular-animate/angular-animate.js',
      '<%=s%>bower_components/angular-aria/angular-aria.js',
      '<%=s%>bower_components/angular-material/angular-material.js'
    ],

    appFiles: [
      '<%=s%>src/flickrItems/FlickrItems.js',
      '<%=s%>src/flickrItems/FlickrItemController.js',
      '<%=s%>src/flickrItems/FlickrItemService.js'
    ],

    /**
     * Uglification (minification) setup. Uglified files are built to the path defined by the d variable and get a .min suffix
     */
    uglify: {
      dependencies: {
        files: {
          '<%= d %>js/<%= dependencyName %>.min.js': '<%= angularFiles %>'
        }
      },
      app: {
        options: {
          banner: '<%= banner %>'
        },
        files: {
          '<%= d %>js/<%= filename %>.min.js': '<%= appFiles %>'
        }
      }
    },

    /**
     * Jasmine unit test setup. Includes Istanbul code coverage setup with Coveralls-friendly output
     */
    jasmine: {
      src: '<%= uglify.app.files %>',
      options: {
        vendor: [
            '<%=s%>bower_components/angular/angular.js',
            '<%=s%>bower_components/angular-animate/angular-animate.js',
            '<%=s%>bower_components/angular-aria/angular-aria.js',
            '<%=s%>bower_components/angular-material/angular-material.js',
          ],
        specs: '<%=t%>**/*.js',
        template: require('grunt-template-jasmine-istanbul'),
        templateOptions: {
          coverage: 'coverage/coverage.json',
          report: [
            { type: 'lcov', options: { dir: 'coverage' }},      // Create .lcov report, required by Coveralls
            { type: 'html', options: { dir: 'coverage/html' }}, // Create an html report, readable by humans
            { type: 'text-summary' }                            // Output results to console post-build
          ],
          thresholds: {
            // Test result thresholds all set to 0 to begin with. Commented values are suggestions
            lines: 0, // 75
            statements: 0, // 75
            branches: 0, // 75
            functions: 0 // 75
          }
        }
      }
    },

    /**
     * JSHint static analysis setup
     */
    jshint: {
      files: ['gruntfile.js', '<%=s%>src/**/*.js', '<%=t%>**/*.js'], // Analyse this file and all source and test files for errors
      options: {
        browser: true, // Assume general browser globals
        globals: {
          predef: []   // Any global variables go here, if required
        }
      }
    },

    /**
     * Less setup
     */
    less: {
      dev: {
        options: {
            paths: ['<%= s %>less/**/*.less'], // Process all Less files in Less folder
        },
        files: {
          "<%= s %>assets/app.css": "<%= s %>less/_styles.less" // Build app.css based on _styles.less
        }
      }
    },

    /**
     * Watch setup. The configured tasks will run when JSHint or Less source files are changed
     */
    watch: {
      files: ['<%= jshint.files %>', '<%= less.dev.options.paths %>'],
      tasks: ['less', 'jshint', 'jasmine']
    },

    /**
     * Coveralls setup. Tells Coveralls where to find code coverage information
     */
    coveralls: {
      options: {
        force: true
      },
      src: 'coverage/lcov.info'
    },

    /**
     * Copy setup
     */
    copy: {
      svg: {
        files: [
          {
            expand: true,
            cwd: '<%= s %>assets/svg',
            src: '**/*',
            dest: '<%= d %>svg',
            filter: 'isFile'
          }
        ]
      },
      css: {
        files: [
          {
            expand: true,
            cwd: '<%= s %>assets',
            src: '*.css',
            dest: '<%= d %>css',
            filter: 'isFile'
          }
        ]
      }
    },

  });

  // Load tasks in this order
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-coveralls');
  grunt.loadNpmTasks('grunt-contrib-copy');

  // Register test and build tasks.These can be run from the command line with "grunt test" or "grunt build"
  // "grunt watch" should be run while developing to notify you when things go wrong
  grunt.registerTask('test', ['jshint', 'jasmine', 'coveralls']);
  grunt.registerTask('build', ['jshint', 'jasmine', 'copy', 'uglify']);

};<commit_msg>Add missing dependency file to gruntfile<commit_after>module.exports = function(grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),       // Read in package variable from package.json

    // Construct a banner containing package and build information
    banner: '/* <%= pkg.name %> | <%= pkg.url %> | <%= pkg.license %>\n' +
            ' * <%= pkg.author %> | <%= pkg.contact %>\n' +
            ' */\n',

    filename: '<%= pkg.name %>',

    dependencyName: 'angular-package',

    s: 'src/',  // The source directory
    d: 'app/',  // The distributable directory, where built files will end up
    t: 'test/', // The test directory, for unit test files/specs

    angularFiles: [
      '<%=s%>bower_components/angular/angular.js',
      '<%=s%>bower_components/angular-animate/angular-animate.js',
      '<%=s%>bower_components/angular-aria/angular-aria.js',
      '<%=s%>bower_components/angular-material/angular-material.js',
      '<%=s%>bower_components/angular-sanitize/angular-sanitize.js'
    ],

    appFiles: [
      '<%=s%>src/flickrItems/FlickrItems.js',
      '<%=s%>src/flickrItems/FlickrItemController.js',
      '<%=s%>src/flickrItems/FlickrItemService.js'
    ],

    /**
     * Uglification (minification) setup. Uglified files are built to the path defined by the d variable and get a .min suffix
     */
    uglify: {
      dependencies: {
        files: {
          '<%= d %>js/<%= dependencyName %>.min.js': '<%= angularFiles %>'
        }
      },
      app: {
        options: {
          banner: '<%= banner %>'
        },
        files: {
          '<%= d %>js/<%= filename %>.min.js': '<%= appFiles %>'
        }
      }
    },

    /**
     * Jasmine unit test setup. Includes Istanbul code coverage setup with Coveralls-friendly output
     */
    jasmine: {
      src: '<%= uglify.app.files %>',
      options: {
        vendor: [
            '<%=s%>bower_components/angular/angular.js',
            '<%=s%>bower_components/angular-animate/angular-animate.js',
            '<%=s%>bower_components/angular-aria/angular-aria.js',
            '<%=s%>bower_components/angular-material/angular-material.js',
          ],
        specs: '<%=t%>**/*.js',
        template: require('grunt-template-jasmine-istanbul'),
        templateOptions: {
          coverage: 'coverage/coverage.json',
          report: [
            { type: 'lcov', options: { dir: 'coverage' }},      // Create .lcov report, required by Coveralls
            { type: 'html', options: { dir: 'coverage/html' }}, // Create an html report, readable by humans
            { type: 'text-summary' }                            // Output results to console post-build
          ],
          thresholds: {
            // Test result thresholds all set to 0 to begin with. Commented values are suggestions
            lines: 0, // 75
            statements: 0, // 75
            branches: 0, // 75
            functions: 0 // 75
          }
        }
      }
    },

    /**
     * JSHint static analysis setup
     */
    jshint: {
      files: ['gruntfile.js', '<%=s%>src/**/*.js', '<%=t%>**/*.js'], // Analyse this file and all source and test files for errors
      options: {
        browser: true, // Assume general browser globals
        globals: {
          predef: []   // Any global variables go here, if required
        }
      }
    },

    /**
     * Less setup
     */
    less: {
      dev: {
        options: {
            paths: ['<%= s %>less/**/*.less'], // Process all Less files in Less folder
        },
        files: {
          "<%= s %>assets/app.css": "<%= s %>less/_styles.less" // Build app.css based on _styles.less
        }
      }
    },

    /**
     * Watch setup. The configured tasks will run when JSHint or Less source files are changed
     */
    watch: {
      files: ['<%= jshint.files %>', '<%= less.dev.options.paths %>'],
      tasks: ['less', 'jshint', 'jasmine']
    },

    /**
     * Coveralls setup. Tells Coveralls where to find code coverage information
     */
    coveralls: {
      options: {
        force: true
      },
      src: 'coverage/lcov.info'
    },

    /**
     * Copy setup
     */
    copy: {
      svg: {
        files: [
          {
            expand: true,
            cwd: '<%= s %>assets/svg',
            src: '**/*',
            dest: '<%= d %>svg',
            filter: 'isFile'
          }
        ]
      },
      css: {
        files: [
          {
            expand: true,
            cwd: '<%= s %>assets',
            src: '*.css',
            dest: '<%= d %>css',
            filter: 'isFile'
          }
        ]
      }
    },

  });

  // Load tasks in this order
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-coveralls');
  grunt.loadNpmTasks('grunt-contrib-copy');

  // Register test and build tasks.These can be run from the command line with "grunt test" or "grunt build"
  // "grunt watch" should be run while developing to notify you when things go wrong
  grunt.registerTask('test', ['jshint', 'jasmine', 'coveralls']);
  grunt.registerTask('build', ['jshint', 'jasmine', 'copy', 'uglify']);

};<|endoftext|>


<commit_before>document.addEventListener('DOMContentLoaded', function () {

    M.Dropdown.init(
        document.querySelectorAll('.dropdown-trigger'),
        {
            constrainWidth: false
        }
    );

    const chosenGroupsInput = document.getElementById("groups-chosen-input")
    const persistedChosenGroups = chosenGroupsInput.value.split(",")

    for (const groupOption of document.getElementsByClassName("group-option")) {
        groupOption.onclick = () => {
            chooseGroup(groupOption)
        }
        if(persistedChosenGroups.includes(groupOption.dataset.groupId)){
            chooseGroup(groupOption)
        }
    }

}, false);

// Add chosen group to collection
chooseGroup = (chosenGroupElement) => {

    const chosenGroup = chosenGroupElement.textContent

    const groupsChosen = document.getElementById("groups-chosen")
    groupsChosen.style.display = "block"

    const groupItem = document.createElement("div")
    groupItem.classList.add("collection-item", "groups-chosen-item")
    groupItem.dataset.groupId = chosenGroupElement.dataset.groupId
    groupItem.value = chosenGroup

    const groupTitle = document.createElement("span")
    groupTitle.className = "groups-chosen-item-title"
    groupTitle.textContent = (groupsChosen.children.length + 1) + ". " + chosenGroup
    groupItem.appendChild(groupTitle)

    const removeButton = document.createElement("span")
    removeButton.className = "material-icons md-24 badge group-option-remove"
    removeButton.textContent = "clear"
    removeButton.onclick = () => {
        groupItem.remove()
        updateGroupsPriority()
        updateChosenGroups()
        if (groupsChosen.children.length === 0) {
            groupsChosen.style.display = "none"
        }
        // Re-enable option to select
        chosenGroupElement.classList.remove("disabled")
    }
    groupItem.appendChild(removeButton)

    groupsChosen.appendChild(groupItem)

    // Disable option from select dropdown (prevent duplicates)
    chosenGroupElement.classList.add("disabled")

    updateChosenGroups()

}

// Update chosen group items with correct priority numbers
updateGroupsPriority = () => {

    const groupsChosen = document.getElementById("groups-chosen")
    for (let i = 0; i < groupsChosen.children.length; i++) {
        const group = groupsChosen.children.item(i)
        const groupTitle = group.getElementsByClassName("groups-chosen-item-title")[0]
        groupTitle.textContent = (i + 1) + ". " + group.value
    }

}

// Update hidden text input field with chosen group ids
updateChosenGroups = () => {

    let chosenGroupsString = ""

    for (const chosenGroup of document.getElementsByClassName("groups-chosen-item")) {
        const groupId = chosenGroup.dataset.groupId
        if (chosenGroupsString === ""){
            chosenGroupsString = groupId
        }else{
            chosenGroupsString += "," + groupId
        }
    }

    const chosenGroupsInput = document.getElementById("groups-chosen-input")
    chosenGroupsInput.value = chosenGroupsString

}
<commit_msg>Fixed group choice input persistance on invalid form<commit_after>document.addEventListener('DOMContentLoaded', function () {

    M.Dropdown.init(
        document.querySelectorAll('.dropdown-trigger'),
        {
            constrainWidth: false
        }
    );

    // Convert persisted group id array string to actual array
    const persistedChosenGroups = document.getElementById("groups-chosen-input").value.match(/\d+/g)

    for (const groupOption of document.getElementsByClassName("group-option")) {
        groupOption.onclick = () => {
            chooseGroup(groupOption)
        }
        if(persistedChosenGroups.includes(groupOption.dataset.groupId)){
            chooseGroup(groupOption)
        }
    }

}, false);

// Add chosen group to collection
chooseGroup = (chosenGroupElement) => {

    const chosenGroup = chosenGroupElement.textContent

    const groupsChosen = document.getElementById("groups-chosen")
    groupsChosen.style.display = "block"

    const groupItem = document.createElement("div")
    groupItem.classList.add("collection-item", "groups-chosen-item")
    groupItem.dataset.groupId = chosenGroupElement.dataset.groupId
    groupItem.value = chosenGroup

    const groupTitle = document.createElement("span")
    groupTitle.className = "groups-chosen-item-title"
    groupTitle.textContent = (groupsChosen.children.length + 1) + ". " + chosenGroup
    groupItem.appendChild(groupTitle)

    const removeButton = document.createElement("span")
    removeButton.className = "material-icons md-24 badge group-option-remove"
    removeButton.textContent = "clear"
    removeButton.onclick = () => {
        groupItem.remove()
        updateGroupsPriority()
        updateChosenGroups()
        if (groupsChosen.children.length === 0) {
            groupsChosen.style.display = "none"
        }
        // Re-enable option to select
        chosenGroupElement.classList.remove("disabled")
    }
    groupItem.appendChild(removeButton)

    groupsChosen.appendChild(groupItem)

    // Disable option from select dropdown (prevent duplicates)
    chosenGroupElement.classList.add("disabled")

    updateChosenGroups()

}

// Update chosen group items with correct priority numbers
updateGroupsPriority = () => {

    const groupsChosen = document.getElementById("groups-chosen")
    for (let i = 0; i < groupsChosen.children.length; i++) {
        const group = groupsChosen.children.item(i)
        const groupTitle = group.getElementsByClassName("groups-chosen-item-title")[0]
        groupTitle.textContent = (i + 1) + ". " + group.value
    }

}

// Update hidden text input field with chosen group ids
updateChosenGroups = () => {

    let chosenGroupsString = ""

    for (const chosenGroup of document.getElementsByClassName("groups-chosen-item")) {
        const groupId = chosenGroup.dataset.groupId
        if (chosenGroupsString === ""){
            chosenGroupsString = groupId
        }else{
            chosenGroupsString += "," + groupId
        }
    }

    const chosenGroupsInput = document.getElementById("groups-chosen-input")
    chosenGroupsInput.value = chosenGroupsString

}
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import './EditRoom.css'

export default class EditRoom extends Component {
  static propTypes = {
    room: PropTypes.object,
    // actions
    closeRoomEditor: PropTypes.func.isRequired,
    createRoom: PropTypes.func.isRequired,
    updateRoom: PropTypes.func.isRequired,
    removeRoom: PropTypes.func.isRequired,
  }

  constructor (props) {
    super(props)
    this.text = React.createRef()
    this.checkbox = React.createRef()
  }

  render () {
    const { room, closeRoomEditor } = this.props

    return (
      <>
        <input type='text'
          placeholder='room name'
          ref={this.text}
          defaultValue={room ? room.name : ''}
          onKeyPress={this.handleKeyPress}
          autoFocus={typeof room === 'undefined'}
          styleName='field'
        />

          <input type='checkbox'
            defaultChecked={!room || room.status === 'open'}
            ref={this.checkbox}
          /> Open

        {!room &&
          <button onClick={this.handleCreateClick} className='primary' styleName='btn'>
            Create Room
          </button>
        }

        {room &&
          <button onClick={this.handleUpdateClick} className='primary' styleName='btn'>
            Update Room
          </button>
        }

        {room &&
          <button onClick={this.handleRemoveClick} styleName='btn'>
            Remove Room
          </button>
        }

        <button onClick={closeRoomEditor}>Cancel</button>
      </>
    )
  }

  handleCreateClick = () => {
    this.props.createRoom({
      name: this.text.current.value,
      status: 'open',
    })
  }

  handleUpdateClick = () => {
    this.props.updateRoom(this.props.room.roomId, {
      name: this.text.current.value,
      status: this.checkbox.current.checked ? 'open' : 'closed',
    })
  }

  handleRemoveClick = () => {
    if (confirm(`Remove room "${this.props.room.name}"?`)) {
      this.props.removeRoom(this.props.room.roomId)
    }
  }

  handleKeyPress = (e) => {
    if (e.charCode === 13) {
      this.props.room ? this.handleUpdateClick() : this.handleCreateClick()
    }
  }
}
<commit_msg>Fix status not respected when creating new room<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import './EditRoom.css'

export default class EditRoom extends Component {
  static propTypes = {
    room: PropTypes.object,
    // actions
    closeRoomEditor: PropTypes.func.isRequired,
    createRoom: PropTypes.func.isRequired,
    updateRoom: PropTypes.func.isRequired,
    removeRoom: PropTypes.func.isRequired,
  }

  constructor (props) {
    super(props)
    this.text = React.createRef()
    this.checkbox = React.createRef()
  }

  render () {
    const { room, closeRoomEditor } = this.props

    return (
      <>
        <input type='text'
          placeholder='room name'
          ref={this.text}
          defaultValue={room ? room.name : ''}
          onKeyPress={this.handleKeyPress}
          autoFocus={typeof room === 'undefined'}
          styleName='field'
        />

          <input type='checkbox'
            defaultChecked={!room || room.status === 'open'}
            ref={this.checkbox}
          /> Open

        {!room &&
          <button onClick={this.handleCreateClick} className='primary' styleName='btn'>
            Create Room
          </button>
        }

        {room &&
          <button onClick={this.handleUpdateClick} className='primary' styleName='btn'>
            Update Room
          </button>
        }

        {room &&
          <button onClick={this.handleRemoveClick} styleName='btn'>
            Remove Room
          </button>
        }

        <button onClick={closeRoomEditor}>Cancel</button>
      </>
    )
  }

  handleCreateClick = () => {
    this.props.createRoom({
      name: this.text.current.value,
      status: this.checkbox.current.checked ? 'open' : 'closed',
    })
  }

  handleUpdateClick = () => {
    this.props.updateRoom(this.props.room.roomId, {
      name: this.text.current.value,
      status: this.checkbox.current.checked ? 'open' : 'closed',
    })
  }

  handleRemoveClick = () => {
    if (confirm(`Remove room "${this.props.room.name}"?`)) {
      this.props.removeRoom(this.props.room.roomId)
    }
  }

  handleKeyPress = (e) => {
    if (e.charCode === 13) {
      this.props.room ? this.handleUpdateClick() : this.handleCreateClick()
    }
  }
}
<|endoftext|>


<commit_before>// CONFIG START
var port = 8080;
// CONFIG END

var restify = require('restify');
var s3 = require('s3');

var s3ClientOptions;
try {
    awsOptions = require('./conf/aws');
} catch(e) {
    console.log('ERROR: API configuration is missing!');
    console.log('Please rename conf/api.js.example to conf/api.js and edit the file to your needs.');
    process.exit(1);
}

var server = restify.createServer();
var s3_client = s3.createClient(awsOptions.s3ClientConfig);

var fileNames = [];

var objectLister = s3_client.listObjects(awsOptions.s3ListObjectConfig);
objectLister.on('error', function(err) {
    console.log('ERROR: There was an issue getting a list of objects from the S3 bucket:');
    console.log(err);
});
objectLister.on('data', function(data) {
    for(var i = 0; i < data.Contents.length; ++i) {
        fileNames.push(data.Contents[i].Key);
    }
});

// ROUTE START
server.get('/', function(req, res, next) {
    res.send(fileNames);
    next();
});
// ROUTE END

server.listen(port, function() {
    console.log('Server is now online at: %s', server.url);
});<commit_msg>now using conf/api.js in server.js<commit_after>var restify = require('restify');
var s3 = require('s3');

var apiOptions;
try {
    apiOptions = require('./conf/api');
} catch(e) {
    console.log('ERROR: API configuration is missing!');
    console.log('Please rename conf/api.js.example to conf/api.js and edit the file to your needs.');
    process.exit(1);
}
var awsOptions;
try {
    awsOptions = require('./conf/aws');
} catch(e) {
    console.log('ERROR: AWS configuration is missing!');
    console.log('Please rename conf/aws.js.example to conf/aws.js and edit the file to your needs.');
    process.exit(1);
}

var server = restify.createServer();
var s3_client = s3.createClient(awsOptions.s3ClientConfig);

var fileNames = [];
var objectLister = s3_client.listObjects(awsOptions.s3ListObjectConfig);
objectLister.on('error', function(err) {
    console.log('ERROR: There was an issue getting a list of objects from the S3 bucket:');
    console.log(err);
});
objectLister.on('data', function(data) {
    for(var i = 0; i < data.Contents.length; ++i) {
        fileNames.push(data.Contents[i].Key);
    }
});

// ROUTE START
server.get('/', function(req, res, next) {
    res.send(fileNames);
    next();
});
// ROUTE END

server.listen(apiOptions.port, function() {
    console.log('Server is now online at: %s', server.url);
});<|endoftext|>


<commit_before>import * as util from './util';

/**
 * Parent class for renderers
 *
 * @extends {Observer}
 */
export default class Drawer extends util.Observer {
    /**
     * @param {HTMLElement} container The container node of the wavesurfer instance
     * @param {WavesurferParams} params The wavesurfer initialisation options
     */
    constructor(container, params) {
        super();
        /** @private */
        this.container = container;
        /**
         * @type {WavesurferParams}
         * @private
         */
        this.params = params;
        /**
         * The width of the renderer
         * @type {number}
         */
        this.width = 0;
        /**
         * The height of the renderer
         * @type {number}
         */
        this.height = params.height * this.params.pixelRatio;
        /** @private */
        this.lastPos = 0;
        /**
         * The `<wave>` element which is added to the container
         * @type {HTMLElement}
         */
        this.wrapper = null;
    }

    /**
     * Alias of `util.style`
     *
     * @param {HTMLElement} el The element that the styles will be applied to
     * @param {Object} styles The map of propName: attribute, both are used as-is
     * @return {HTMLElement} el
     */
    style(el, styles) {
        return util.style(el, styles);
    }

    /**
     * Create the wrapper `<wave>` element, style it and set up the events for
     * interaction
     */
    createWrapper() {
        this.wrapper = this.container.appendChild(
            document.createElement('wave')
        );

        this.style(this.wrapper, {
            display: 'block',
            position: 'relative',
            userSelect: 'none',
            webkitUserSelect: 'none',
            height: this.params.height + 'px'
        });

        if (this.params.fillParent || this.params.scrollParent) {
            this.style(this.wrapper, {
                width: '100%',
                overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',
                overflowY: 'hidden'
            });
        }

        this.setupWrapperEvents();
    }

    /**
     * Handle click event
     *
     * @param {Event} e Click event
     * @param {?boolean} noPrevent Set to true to not call `e.preventDefault()`
     * @return {number} Playback position from 0 to 1
     */
    handleEvent(e, noPrevent) {
        !noPrevent && e.preventDefault();

        const clientX = e.targetTouches
            ? e.targetTouches[0].clientX
            : e.clientX;
        const bbox = this.wrapper.getBoundingClientRect();

        const nominalWidth = this.width;
        const parentWidth = this.getWidth();

        let progress;
        if (!this.params.fillParent && nominalWidth < parentWidth) {
            progress =
                (this.params.rtl ? bbox.right - clientX : clientX - bbox.left) *
                    (this.params.pixelRatio / nominalWidth) || 0;

            if (progress > 1) {
                progress = 1;
            }
        } else {
            progress =
                ((this.params.rtl
                    ? bbox.right - clientX
                    : clientX - bbox.left) +
                    this.wrapper.scrollLeft) /
                    this.wrapper.scrollWidth || 0;
        }

        return progress;
    }

    /**
     * @private
     */
    setupWrapperEvents() {
        this.wrapper.addEventListener('click', e => {
            const scrollbarHeight =
                this.wrapper.offsetHeight - this.wrapper.clientHeight;
            if (scrollbarHeight !== 0) {
                // scrollbar is visible.  Check if click was on it
                const bbox = this.wrapper.getBoundingClientRect();
                if (e.clientY >= bbox.bottom - scrollbarHeight) {
                    // ignore mousedown as it was on the scrollbar
                    return;
                }
            }

            if (this.params.interact) {
                this.fireEvent('click', e, this.handleEvent(e));
            }
        });

        this.wrapper.addEventListener('dblclick', e => {
            if (this.params.interact) {
                this.fireEvent('dblclick', e);
            }
        });

        this.wrapper.addEventListener('scroll', e =>
            this.fireEvent('scroll', e)
        );
    }

    /**
     * Draw peaks on the canvas
     *
     * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
     * for split channel rendering
     * @param {number} length The width of the area that should be drawn
     * @param {number} start The x-offset of the beginning of the area that
     * should be rendered
     * @param {number} end The x-offset of the end of the area that should be
     * rendered
     */
    drawPeaks(peaks, length, start, end) {
        if (!this.setWidth(length)) {
            this.clearWave();
        }

        this.params.barWidth
            ? this.drawBars(peaks, 0, start, end)
            : this.drawWave(peaks, 0, start, end);
    }

    /**
     * Scroll to the beginning
     */
    resetScroll() {
        if (this.wrapper !== null) {
            this.wrapper.scrollLeft = 0;
        }
    }

    /**
     * Recenter the view-port at a certain percent of the waveform
     *
     * @param {number} percent Value from 0 to 1 on the waveform
     */
    recenter(percent) {
        const position = this.wrapper.scrollWidth * percent;
        this.recenterOnPosition(position, true);
    }

    /**
     * Recenter the view-port on a position, either scroll there immediately or
     * in steps of 5 pixels
     *
     * @param {number} position X-offset in pixels
     * @param {boolean} immediate Set to true to immediately scroll somewhere
     */
    recenterOnPosition(position, immediate) {
        const scrollLeft = this.wrapper.scrollLeft;
        const half = ~~(this.wrapper.clientWidth / 2);
        const maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
        let target = position - half;
        let offset = target - scrollLeft;

        if (maxScroll == 0) {
            // no need to continue if scrollbar is not there
            return;
        }

        // if the cursor is currently visible...
        if (!immediate && -half <= offset && offset < half) {
            // set rate at which waveform is centered
            let rate = this.params.autoCenterRate;

            // make rate depend on width of view and length of waveform
            rate /= half;
            rate *= maxScroll;

            offset = Math.max(-rate, Math.min(rate, offset));
            target = scrollLeft + offset;
        }

        // limit target to valid range (0 to maxScroll)
        target = Math.max(0, Math.min(maxScroll, target));
        // no use attempting to scroll if we're not moving
        if (target != scrollLeft) {
            this.wrapper.scrollLeft = target;
        }
    }

    /**
     * Get the current scroll position in pixels
     *
     * @return {number} Horizontal scroll position in pixels
     */
    getScrollX() {
        let x = 0;
        if (this.wrapper) {
            const pixelRatio = this.params.pixelRatio;
            x = Math.round(this.wrapper.scrollLeft * pixelRatio);

            // In cases of elastic scroll (safari with mouse wheel) you can
            // scroll beyond the limits of the container
            // Calculate and floor the scrollable extent to make sure an out
            // of bounds value is not returned
            // Ticket #1312
            if (this.params.scrollParent) {
                const maxScroll = ~~(
                    this.wrapper.scrollWidth * pixelRatio -
                    this.getWidth()
                );
                x = Math.min(maxScroll, Math.max(0, x));
            }
        }
        return x;
    }

    /**
     * Get the width of the container
     *
     * @return {number} The width of the container
     */
    getWidth() {
        return Math.round(this.container.clientWidth * this.params.pixelRatio);
    }

    /**
     * Set the width of the container
     *
     * @param {number} width The new width of the container
     * @return {boolean} Whether the width of the container was updated or not
     */
    setWidth(width) {
        if (this.width == width) {
            return false;
        }

        this.width = width;

        if (this.params.fillParent || this.params.scrollParent) {
            this.style(this.wrapper, {
                width: ''
            });
        } else {
            this.style(this.wrapper, {
                width: ~~(this.width / this.params.pixelRatio) + 'px'
            });
        }

        this.updateSize();
        return true;
    }

    /**
     * Set the height of the container
     *
     * @param {number} height The new height of the container.
     * @return {boolean} Whether the height of the container was updated or not
     */
    setHeight(height) {
        if (height == this.height) {
            return false;
        }
        this.height = height;

        this.style(this.wrapper, {
            height: ~~(this.height / this.params.pixelRatio) + 'px'
        });

        this.updateSize();
        return true;
    }

    /**
     * Called by wavesurfer when progress should be rendered
     *
     * @param {number} progress From 0 to 1
     */
    progress(progress) {
        const minPxDelta = 1 / this.params.pixelRatio;
        const pos = Math.round(progress * this.width) * minPxDelta;

        if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) {
            this.lastPos = pos;

            if (this.params.scrollParent && this.params.autoCenter) {
                const newPos = ~~(this.wrapper.scrollWidth * progress);
                this.recenterOnPosition(
                    newPos,
                    this.params.autoCenterImmediately
                );
            }

            this.updateProgress(pos);
        }
    }

    /**
     * This is called when wavesurfer is destroyed
     */
    destroy() {
        this.unAll();
        if (this.wrapper) {
            if (this.wrapper.parentNode == this.container) {
                this.container.removeChild(this.wrapper);
            }
            this.wrapper = null;
        }
    }

    /* Renderer-specific methods */

    /**
     * Called after cursor related params have changed.
     *
     * @abstract
     */
    updateCursor() {}

    /**
     * Called when the size of the container changes so the renderer can adjust
     *
     * @abstract
     */
    updateSize() {}

    /**
     * Draw a waveform with bars
     *
     * @abstract
     * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
     * rendering
     * @param {number} channelIndex The index of the current channel. Normally
     * should be 0
     * @param {number} start The x-offset of the beginning of the area that
     * should be rendered
     * @param {number} end The x-offset of the end of the area that should be
     * rendered
     */
    drawBars(peaks, channelIndex, start, end) {}

    /**
     * Draw a waveform
     *
     * @abstract
     * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
     * rendering
     * @param {number} channelIndex The index of the current channel. Normally
     * should be 0
     * @param {number} start The x-offset of the beginning of the area that
     * should be rendered
     * @param {number} end The x-offset of the end of the area that should be
     * rendered
     */
    drawWave(peaks, channelIndex, start, end) {}

    /**
     * Clear the waveform
     *
     * @abstract
     */
    clearWave() {}

    /**
     * Render the new progress
     *
     * @abstract
     * @param {number} position X-Offset of progress position in pixels
     */
    updateProgress(position) {}
}
<commit_msg>expose progress to the dblclick event (#1790)<commit_after>import * as util from './util';

/**
 * Parent class for renderers
 *
 * @extends {Observer}
 */
export default class Drawer extends util.Observer {
    /**
     * @param {HTMLElement} container The container node of the wavesurfer instance
     * @param {WavesurferParams} params The wavesurfer initialisation options
     */
    constructor(container, params) {
        super();
        /** @private */
        this.container = container;
        /**
         * @type {WavesurferParams}
         * @private
         */
        this.params = params;
        /**
         * The width of the renderer
         * @type {number}
         */
        this.width = 0;
        /**
         * The height of the renderer
         * @type {number}
         */
        this.height = params.height * this.params.pixelRatio;
        /** @private */
        this.lastPos = 0;
        /**
         * The `<wave>` element which is added to the container
         * @type {HTMLElement}
         */
        this.wrapper = null;
    }

    /**
     * Alias of `util.style`
     *
     * @param {HTMLElement} el The element that the styles will be applied to
     * @param {Object} styles The map of propName: attribute, both are used as-is
     * @return {HTMLElement} el
     */
    style(el, styles) {
        return util.style(el, styles);
    }

    /**
     * Create the wrapper `<wave>` element, style it and set up the events for
     * interaction
     */
    createWrapper() {
        this.wrapper = this.container.appendChild(
            document.createElement('wave')
        );

        this.style(this.wrapper, {
            display: 'block',
            position: 'relative',
            userSelect: 'none',
            webkitUserSelect: 'none',
            height: this.params.height + 'px'
        });

        if (this.params.fillParent || this.params.scrollParent) {
            this.style(this.wrapper, {
                width: '100%',
                overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',
                overflowY: 'hidden'
            });
        }

        this.setupWrapperEvents();
    }

    /**
     * Handle click event
     *
     * @param {Event} e Click event
     * @param {?boolean} noPrevent Set to true to not call `e.preventDefault()`
     * @return {number} Playback position from 0 to 1
     */
    handleEvent(e, noPrevent) {
        !noPrevent && e.preventDefault();

        const clientX = e.targetTouches
            ? e.targetTouches[0].clientX
            : e.clientX;
        const bbox = this.wrapper.getBoundingClientRect();

        const nominalWidth = this.width;
        const parentWidth = this.getWidth();

        let progress;
        if (!this.params.fillParent && nominalWidth < parentWidth) {
            progress =
                (this.params.rtl ? bbox.right - clientX : clientX - bbox.left) *
                    (this.params.pixelRatio / nominalWidth) || 0;

            if (progress > 1) {
                progress = 1;
            }
        } else {
            progress =
                ((this.params.rtl
                    ? bbox.right - clientX
                    : clientX - bbox.left) +
                    this.wrapper.scrollLeft) /
                    this.wrapper.scrollWidth || 0;
        }

        return progress;
    }

    /**
     * @private
     */
    setupWrapperEvents() {
        this.wrapper.addEventListener('click', e => {
            const scrollbarHeight =
                this.wrapper.offsetHeight - this.wrapper.clientHeight;
            if (scrollbarHeight !== 0) {
                // scrollbar is visible.  Check if click was on it
                const bbox = this.wrapper.getBoundingClientRect();
                if (e.clientY >= bbox.bottom - scrollbarHeight) {
                    // ignore mousedown as it was on the scrollbar
                    return;
                }
            }

            if (this.params.interact) {
                this.fireEvent('click', e, this.handleEvent(e));
            }
        });

        this.wrapper.addEventListener('dblclick', e => {
            if (this.params.interact) {
                this.fireEvent('dblclick', e, this.handleEvent(e));
            }
        });

        this.wrapper.addEventListener('scroll', e =>
            this.fireEvent('scroll', e)
        );
    }

    /**
     * Draw peaks on the canvas
     *
     * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
     * for split channel rendering
     * @param {number} length The width of the area that should be drawn
     * @param {number} start The x-offset of the beginning of the area that
     * should be rendered
     * @param {number} end The x-offset of the end of the area that should be
     * rendered
     */
    drawPeaks(peaks, length, start, end) {
        if (!this.setWidth(length)) {
            this.clearWave();
        }

        this.params.barWidth
            ? this.drawBars(peaks, 0, start, end)
            : this.drawWave(peaks, 0, start, end);
    }

    /**
     * Scroll to the beginning
     */
    resetScroll() {
        if (this.wrapper !== null) {
            this.wrapper.scrollLeft = 0;
        }
    }

    /**
     * Recenter the view-port at a certain percent of the waveform
     *
     * @param {number} percent Value from 0 to 1 on the waveform
     */
    recenter(percent) {
        const position = this.wrapper.scrollWidth * percent;
        this.recenterOnPosition(position, true);
    }

    /**
     * Recenter the view-port on a position, either scroll there immediately or
     * in steps of 5 pixels
     *
     * @param {number} position X-offset in pixels
     * @param {boolean} immediate Set to true to immediately scroll somewhere
     */
    recenterOnPosition(position, immediate) {
        const scrollLeft = this.wrapper.scrollLeft;
        const half = ~~(this.wrapper.clientWidth / 2);
        const maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
        let target = position - half;
        let offset = target - scrollLeft;

        if (maxScroll == 0) {
            // no need to continue if scrollbar is not there
            return;
        }

        // if the cursor is currently visible...
        if (!immediate && -half <= offset && offset < half) {
            // set rate at which waveform is centered
            let rate = this.params.autoCenterRate;

            // make rate depend on width of view and length of waveform
            rate /= half;
            rate *= maxScroll;

            offset = Math.max(-rate, Math.min(rate, offset));
            target = scrollLeft + offset;
        }

        // limit target to valid range (0 to maxScroll)
        target = Math.max(0, Math.min(maxScroll, target));
        // no use attempting to scroll if we're not moving
        if (target != scrollLeft) {
            this.wrapper.scrollLeft = target;
        }
    }

    /**
     * Get the current scroll position in pixels
     *
     * @return {number} Horizontal scroll position in pixels
     */
    getScrollX() {
        let x = 0;
        if (this.wrapper) {
            const pixelRatio = this.params.pixelRatio;
            x = Math.round(this.wrapper.scrollLeft * pixelRatio);

            // In cases of elastic scroll (safari with mouse wheel) you can
            // scroll beyond the limits of the container
            // Calculate and floor the scrollable extent to make sure an out
            // of bounds value is not returned
            // Ticket #1312
            if (this.params.scrollParent) {
                const maxScroll = ~~(
                    this.wrapper.scrollWidth * pixelRatio -
                    this.getWidth()
                );
                x = Math.min(maxScroll, Math.max(0, x));
            }
        }
        return x;
    }

    /**
     * Get the width of the container
     *
     * @return {number} The width of the container
     */
    getWidth() {
        return Math.round(this.container.clientWidth * this.params.pixelRatio);
    }

    /**
     * Set the width of the container
     *
     * @param {number} width The new width of the container
     * @return {boolean} Whether the width of the container was updated or not
     */
    setWidth(width) {
        if (this.width == width) {
            return false;
        }

        this.width = width;

        if (this.params.fillParent || this.params.scrollParent) {
            this.style(this.wrapper, {
                width: ''
            });
        } else {
            this.style(this.wrapper, {
                width: ~~(this.width / this.params.pixelRatio) + 'px'
            });
        }

        this.updateSize();
        return true;
    }

    /**
     * Set the height of the container
     *
     * @param {number} height The new height of the container.
     * @return {boolean} Whether the height of the container was updated or not
     */
    setHeight(height) {
        if (height == this.height) {
            return false;
        }
        this.height = height;

        this.style(this.wrapper, {
            height: ~~(this.height / this.params.pixelRatio) + 'px'
        });

        this.updateSize();
        return true;
    }

    /**
     * Called by wavesurfer when progress should be rendered
     *
     * @param {number} progress From 0 to 1
     */
    progress(progress) {
        const minPxDelta = 1 / this.params.pixelRatio;
        const pos = Math.round(progress * this.width) * minPxDelta;

        if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) {
            this.lastPos = pos;

            if (this.params.scrollParent && this.params.autoCenter) {
                const newPos = ~~(this.wrapper.scrollWidth * progress);
                this.recenterOnPosition(
                    newPos,
                    this.params.autoCenterImmediately
                );
            }

            this.updateProgress(pos);
        }
    }

    /**
     * This is called when wavesurfer is destroyed
     */
    destroy() {
        this.unAll();
        if (this.wrapper) {
            if (this.wrapper.parentNode == this.container) {
                this.container.removeChild(this.wrapper);
            }
            this.wrapper = null;
        }
    }

    /* Renderer-specific methods */

    /**
     * Called after cursor related params have changed.
     *
     * @abstract
     */
    updateCursor() {}

    /**
     * Called when the size of the container changes so the renderer can adjust
     *
     * @abstract
     */
    updateSize() {}

    /**
     * Draw a waveform with bars
     *
     * @abstract
     * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
     * rendering
     * @param {number} channelIndex The index of the current channel. Normally
     * should be 0
     * @param {number} start The x-offset of the beginning of the area that
     * should be rendered
     * @param {number} end The x-offset of the end of the area that should be
     * rendered
     */
    drawBars(peaks, channelIndex, start, end) {}

    /**
     * Draw a waveform
     *
     * @abstract
     * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
     * rendering
     * @param {number} channelIndex The index of the current channel. Normally
     * should be 0
     * @param {number} start The x-offset of the beginning of the area that
     * should be rendered
     * @param {number} end The x-offset of the end of the area that should be
     * rendered
     */
    drawWave(peaks, channelIndex, start, end) {}

    /**
     * Clear the waveform
     *
     * @abstract
     */
    clearWave() {}

    /**
     * Render the new progress
     *
     * @abstract
     * @param {number} position X-Offset of progress position in pixels
     */
    updateProgress(position) {}
}
<|endoftext|>


<commit_before>import formidable from 'formidable'
import path from 'path'
import Util from './util'
import config from 'gen/config'
import zipper from 'zip-local'
import Hook from 'util/hook'
import {execSync} from 'child_process'
import {
    CAPABILITY_MANAGE_BACKUPS
} from 'util/capabilities'
import {decodeToken} from './util/jwt'
const {UPLOAD_DIR, UPLOAD_URL, DEFAULT_LANGUAGE} = config

const beforeUpload = (res, req, upload_dir) => {
    if (!Util.ensureDirectoryExistence(upload_dir)) {
        // no upload folder
        res.writeHead(500, {'content-type': 'application/json'});
        res.end(`{"status":"error","message":"Upload folder coud not be created -> ${upload_dir}"}`)
        return false;
    } else {
        if (req.method.toLowerCase() !== 'post') {

            // Method Not Allowed
            res.writeHead(405, {'content-type': 'application/json'});
            res.end('{"status":"error","message":"Method Not Allowed. You must send data with post method"}')
            return false
        }
    }
    return true;
}

const authContextOrError = async (db, res, req, capability) => {
    // check auth token
    const authContext = decodeToken(req.headers.authorization)

    if (!authContext) {
        // no auth
        res.writeHead(401, {'content-type': 'application/json'});
        res.end('{"status":"error","message":"Upload failed. Unauthorized to upload files"}')
        return null
    } else {

        if (capability && !await Util.userHasCapability(db, authContext, capability)) {
            res.writeHead(401, {'content-type': 'application/json'});
            res.end('{"status":"error","message":"User has no capability to ' + capability + '"}')
            return null
        }

        return authContext
    }
}

export const handleUpload = db => async (req, res) => {

    // make sure upload dir exists
    const upload_dir = path.join(__dirname, '..' + UPLOAD_DIR)
    if (beforeUpload(res, req, upload_dir)) {

        let context = decodeToken(req.headers.authorization)

        if (!context.id) {
            // use anonymouse user
            const anonymousUser = await Util.userByName(db, 'anonymous')
            context = {username: anonymousUser.username, id: anonymousUser._id.toString()}
        }

        if (context.id) {

            if (!context.lang) {
                context.lang = DEFAULT_LANGUAGE
            }

            /* Process the uploads */
            const form = new formidable.IncomingForm()

            form.maxFileSize = 10 * 1024 * 1024 * 1024 // 10GB

            const fileIds = []

            // specify that we want to allow the user to upload multiple files in a single request
            //form.multiples = true

            // send header
            res.writeHead(200, {'content-type': 'application/json'})

            // create map with parameters
            const data = {}
            form.on('field', function (key, value) {
                try {
                    data[key] = JSON.parse(value)
                } catch (e) {
                    console.warn(e, value)
                    data[key] = value
                }
            })

            let endReached = false

            let response = {status:'success'}, count = 0

            // every time a file has been uploaded successfully,
            // rename it to it's orignal name
            form.on('file', async (field, file) => {
                setTimeout(()=> {
                    count++
                    if (Hook.hooks['FileUpload'] && Hook.hooks['FileUpload'].length) {
                        let c = Hook.hooks['FileUpload'].length
                        for (let i = 0; i < Hook.hooks['FileUpload'].length; ++i) {
                            await Hook.hooks['FileUpload'][i].callback({db, context, file, response, data})
                        }
                    }
                    count--
                    if (endReached && count === 0) {
                        res.end(JSON.stringify(response))
                    }
                },1000)
            })

            // log any errors that occur
            form.on('error', function (err) {
                console.log(err)
                res.end('{"status":"error","message":"' + err.message + '"}')
            })
            form.on('aborted', function (err) {
                console.log(err)
                res.end('{"status":"aborted","message":"Upload was aborted"}')
            })

            /*form.on('progress', function (bytesReceived, bytesExpected) {
                const percent = (bytesReceived / bytesExpected * 100) | 0
                console.log('Uploading: ' + percent + '%')
            })*/

            // once all the files have been uploaded, send a response to the client
            form.on('end', () =>{
                endReached = true
            })

            // parse the incoming request containing the form data
            form.parse(req)
        }
    }
}

export const handleMediaDumpUpload = db => async (req, res) => {

    // make sure upload dir exists
    const upload_dir = path.join(__dirname, '../' + UPLOAD_DIR)
    if (beforeUpload(res, req, upload_dir)) {

        const authContext = await authContextOrError(db, res, req, CAPABILITY_MANAGE_BACKUPS)
        if (authContext) {

            /* Process the uploads */
            const form = formidable({maxFileSize: 1014 * 1024 * 1024, keepExtensions:false})

            res.writeHead(200, {'content-type': 'application/json'})

            // every time a file has been uploaded successfully,
            // rename it to it's orignal name
            form.on('file', function (field, file) {
                try {
                    zipper.sync.unzip(file.path).save(upload_dir);
                }catch (e) {
                    console.log(file.path)
                    console.error(e)
                    res.end('{"status":"error","message":"' + e.message + '"}')

                }
            })

            // log any errors that occur
            form.on('error', function (err) {
                console.log(err)
                res.end('{"status":"error","message":"' + err.message + '"}')
            })
            form.on('aborted', function () {
                res.end('{"status":"aborted","message":"Upload was aborted"}')
            })

            form.on('progress', function (bytesReceived, bytesExpected) {
                const percent = (bytesReceived / bytesExpected * 100) | 0
                console.log('Uploading: ' + percent + '%')
            })

            // once all the files have been uploaded, send a response to the client
            form.on('end', function () {
                res.end('{"status":"success"}')
            })

            // parse the incoming request containing the form data
            form.parse(req)
        }
    }
}

export const handleDbDumpUpload = (db, client) => async (req, res) => {

    const authContext = await authContextOrError(db, res, req, CAPABILITY_MANAGE_BACKUPS)
    if (authContext) {

        /* Process the uploads */
        const form = new formidable.IncomingForm()

        res.writeHead(200, {'content-type': 'application/json'})

        // every time a file has been uploaded successfully,
        // rename it to it's orignal name
        form.on('file', function (field, file) {

            console.log(client.s.url)
            // --drop --> drops collections before
            const response = execSync(`mongorestore --db lunuc --uri="${client.s.url}" --drop --gzip --archive="${file.path}"`)
            console.log('restoreDbDump', response)

        })

        // log any errors that occur
        form.on('error', function (err) {
            res.end('{"status":"error","message":"' + err.message + '"}')
        })
        form.on('aborted', function () {
            res.end('{"status":"aborted","message":"Upload was aborted"}')
        })

        form.on('progress', function (bytesReceived, bytesExpected) {
            const percent = (bytesReceived / bytesExpected * 100) | 0
            console.log('Uploading: ' + percent + '%')
        })

        // once all the files have been uploaded, send a response to the client
        form.on('end', function () {
            res.end('{"status":"success"}')
        })

        // parse the incoming request containing the form data
        form.parse(req)
    }
}
<commit_msg>Small improvements<commit_after>import formidable from 'formidable'
import path from 'path'
import Util from './util'
import config from 'gen/config'
import zipper from 'zip-local'
import Hook from 'util/hook'
import {execSync} from 'child_process'
import {
    CAPABILITY_MANAGE_BACKUPS
} from 'util/capabilities'
import {decodeToken} from './util/jwt'
const {UPLOAD_DIR, UPLOAD_URL, DEFAULT_LANGUAGE} = config

const beforeUpload = (res, req, upload_dir) => {
    if (!Util.ensureDirectoryExistence(upload_dir)) {
        // no upload folder
        res.writeHead(500, {'content-type': 'application/json'});
        res.end(`{"status":"error","message":"Upload folder coud not be created -> ${upload_dir}"}`)
        return false;
    } else {
        if (req.method.toLowerCase() !== 'post') {

            // Method Not Allowed
            res.writeHead(405, {'content-type': 'application/json'});
            res.end('{"status":"error","message":"Method Not Allowed. You must send data with post method"}')
            return false
        }
    }
    return true;
}

const authContextOrError = async (db, res, req, capability) => {
    // check auth token
    const authContext = decodeToken(req.headers.authorization)

    if (!authContext) {
        // no auth
        res.writeHead(401, {'content-type': 'application/json'});
        res.end('{"status":"error","message":"Upload failed. Unauthorized to upload files"}')
        return null
    } else {

        if (capability && !await Util.userHasCapability(db, authContext, capability)) {
            res.writeHead(401, {'content-type': 'application/json'});
            res.end('{"status":"error","message":"User has no capability to ' + capability + '"}')
            return null
        }

        return authContext
    }
}

export const handleUpload = db => async (req, res) => {

    // make sure upload dir exists
    const upload_dir = path.join(__dirname, '..' + UPLOAD_DIR)
    if (beforeUpload(res, req, upload_dir)) {

        let context = decodeToken(req.headers.authorization)

        if (!context.id) {
            // use anonymouse user
            const anonymousUser = await Util.userByName(db, 'anonymous')
            context = {username: anonymousUser.username, id: anonymousUser._id.toString()}
        }

        if (context.id) {

            if (!context.lang) {
                context.lang = DEFAULT_LANGUAGE
            }

            /* Process the uploads */
            const form = new formidable.IncomingForm()

            form.maxFileSize = 10 * 1024 * 1024 * 1024 // 10GB

            const fileIds = []

            // specify that we want to allow the user to upload multiple files in a single request
            //form.multiples = true

            // send header
            res.writeHead(200, {'content-type': 'application/json'})

            // create map with parameters
            const data = {}
            form.on('field', function (key, value) {
                try {
                    data[key] = JSON.parse(value)
                } catch (e) {
                    console.warn(e, value)
                    data[key] = value
                }
            })

            let endReached = false

            let response = {status:'success'}, count = 0

            // every time a file has been uploaded successfully,
            // rename it to it's orignal name
            form.on('file', async (field, file) => {
                setTimeout(async()=> {
                    count++
                    if (Hook.hooks['FileUpload'] && Hook.hooks['FileUpload'].length) {
                        let c = Hook.hooks['FileUpload'].length
                        for (let i = 0; i < Hook.hooks['FileUpload'].length; ++i) {
                            await Hook.hooks['FileUpload'][i].callback({db, context, file, response, data})
                        }
                    }
                    count--
                    if (endReached && count === 0) {
                        res.end(JSON.stringify(response))
                    }
                },1000)
            })

            // log any errors that occur
            form.on('error', function (err) {
                console.log(err)
                res.end('{"status":"error","message":"' + err.message + '"}')
            })
            form.on('aborted', function (err) {
                console.log(err)
                res.end('{"status":"aborted","message":"Upload was aborted"}')
            })

            /*form.on('progress', function (bytesReceived, bytesExpected) {
                const percent = (bytesReceived / bytesExpected * 100) | 0
                console.log('Uploading: ' + percent + '%')
            })*/

            // once all the files have been uploaded, send a response to the client
            form.on('end', () =>{
                endReached = true
            })

            // parse the incoming request containing the form data
            form.parse(req)
        }
    }
}

export const handleMediaDumpUpload = db => async (req, res) => {

    // make sure upload dir exists
    const upload_dir = path.join(__dirname, '../' + UPLOAD_DIR)
    if (beforeUpload(res, req, upload_dir)) {

        const authContext = await authContextOrError(db, res, req, CAPABILITY_MANAGE_BACKUPS)
        if (authContext) {

            /* Process the uploads */
            const form = formidable({maxFileSize: 1014 * 1024 * 1024, keepExtensions:false})

            res.writeHead(200, {'content-type': 'application/json'})

            // every time a file has been uploaded successfully,
            // rename it to it's orignal name
            form.on('file', function (field, file) {
                try {
                    zipper.sync.unzip(file.path).save(upload_dir);
                }catch (e) {
                    console.log(file.path)
                    console.error(e)
                    res.end('{"status":"error","message":"' + e.message + '"}')

                }
            })

            // log any errors that occur
            form.on('error', function (err) {
                console.log(err)
                res.end('{"status":"error","message":"' + err.message + '"}')
            })
            form.on('aborted', function () {
                res.end('{"status":"aborted","message":"Upload was aborted"}')
            })

            form.on('progress', function (bytesReceived, bytesExpected) {
                const percent = (bytesReceived / bytesExpected * 100) | 0
                console.log('Uploading: ' + percent + '%')
            })

            // once all the files have been uploaded, send a response to the client
            form.on('end', function () {
                res.end('{"status":"success"}')
            })

            // parse the incoming request containing the form data
            form.parse(req)
        }
    }
}

export const handleDbDumpUpload = (db, client) => async (req, res) => {

    const authContext = await authContextOrError(db, res, req, CAPABILITY_MANAGE_BACKUPS)
    if (authContext) {

        /* Process the uploads */
        const form = new formidable.IncomingForm()

        res.writeHead(200, {'content-type': 'application/json'})

        // every time a file has been uploaded successfully,
        // rename it to it's orignal name
        form.on('file', function (field, file) {

            console.log(client.s.url)
            // --drop --> drops collections before
            const response = execSync(`mongorestore --db lunuc --uri="${client.s.url}" --drop --gzip --archive="${file.path}"`)
            console.log('restoreDbDump', response)

        })

        // log any errors that occur
        form.on('error', function (err) {
            res.end('{"status":"error","message":"' + err.message + '"}')
        })
        form.on('aborted', function () {
            res.end('{"status":"aborted","message":"Upload was aborted"}')
        })

        form.on('progress', function (bytesReceived, bytesExpected) {
            const percent = (bytesReceived / bytesExpected * 100) | 0
            console.log('Uploading: ' + percent + '%')
        })

        // once all the files have been uploaded, send a response to the client
        form.on('end', function () {
            res.end('{"status":"success"}')
        })

        // parse the incoming request containing the form data
        form.parse(req)
    }
}
<|endoftext|>