<commit_before>/*global describe it before after  =*/

"use client";

require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
  function (architect, chai, baseProc) {
    var expect = chai.expect;

    document.body.appendChild(document.createElement("div"))
        .setAttribute("id", "saveStatus");

    expect.setupArchitectTest([
        {
            packagePath : "plugins/c9.core/c9",
            startdate   : new Date(),
            debug       : true,
            hosted      : true,
            local       : false,
            davPrefix   : "/"
        },

        "plugins/c9.core/ext",
        "plugins/c9.core/http",
        "plugins/c9.core/util",
        "plugins/c9.ide.ui/lib_apf",
        "plugins/c9.ide.ui/menus",
        {
            packagePath: "plugins/c9.core/settings",
            testing: true
        },
        "plugins/c9.core/api.js",
        {
            packagePath  : "plugins/c9.ide.ui/ui",
            staticPrefix : "plugins/c9.ide.ui"
        },
        "plugins/c9.ide.editors/document",
        "plugins/c9.ide.editors/undomanager",
        {
            packagePath: "plugins/c9.ide.editors/editors",
            defaultEditor: "texteditor"
        },
        "plugins/c9.ide.editors/editor",
        "plugins/c9.ide.editors/tabmanager",
        "plugins/c9.ide.ui/focus",
        "plugins/c9.ide.editors/pane",
        "plugins/c9.ide.editors/tab",
        "plugins/c9.ide.ace/ace",
        "plugins/c9.ide.save/save",
        "plugins/c9.vfs.client/vfs_client",
        "plugins/c9.vfs.client/endpoint",
        "plugins/c9.ide.auth/auth",
        {
            packagePath: "plugins/c9.fs/fs",
            baseProc: baseProc
        },
        "plugins/c9.fs/fs.cache.xml",

        "plugins/c9.ide.dialog/dialog",
        "plugins/c9.ide.dialog.common/alert",
        "plugins/c9.ide.dialog.common/confirm",
        "plugins/c9.ide.dialog.common/filechange",
        "plugins/c9.ide.dialog.common/fileoverwrite",
        "plugins/c9.ide.dialog.common/fileremove",
        "plugins/c9.ide.dialog.common/question",
        "plugins/c9.ide.dialog.file/filesave",

        // Mock plugins
        {
            consumes : ["apf", "ui", "Plugin"],
            provides : [
                "commands", "menus", "commands", "layout", "watcher",
                "save", "anims", "tree", "preferences", "clipboard",
                "auth.bootstrap", "info", "dialog.error"
            ],
            setup    : expect.html.mocked
        },
        {
            consumes : [],
            provides : ["proc"],
            setup    : expect.html.mocked
        },
        {
            consumes : ["tabManager", "save", "fs", "dialog.filesave", "dialog.question"],
            provides : [],
            setup    : main
        }
    ], architect);

    function main(options, imports, register) {
        var tabs     = imports.tabManager;
        var fs       = imports.fs;
        var save     = imports.save;
        var filesave = imports["dialog.filesave"];
        var question = imports["dialog.question"];

        function countEvents(count, expected, done){
            if (count == expected)
                done();
            else
                throw new Error("Wrong Event Count: "
                    + count + " of " + expected);
        }

        expect.html.setConstructor(function(tab){
            if (typeof tab == "object")
                return tab.pane.aml.getPage("editor::" + tab.editorType).$ext;
        });

        function changeTab(path, done){
            var tab = tabs.findTab(path);
            if (!tab)
                return setTimeout(changeTab.bind(null, path, done), 100);

            tabs.focusTab(tab);
            tab.document.undoManager.once("change", function(){
                expect(tab.document.changed).to.ok;
                done();
            });
            tab.document.editor.ace.insert("test");

            return tab;
        }

        var TIMEOUT = 10;
        var files   = [];
        describe('save', function() {
            this.timeout(6000)

            before(function(done){
                apf.config.setProperty("allow-select", false);
                apf.config.setProperty("allow-blur", false);

                files = ["/save1.txt", "/save2.txt", "/save3.txt"];

                bar.$ext.style.background = "rgba(220, 220, 220, 0.93)";
                bar.$ext.style.position = "fixed";
                bar.$ext.style.left = "20px";
                bar.$ext.style.right = "20px";
                bar.$ext.style.bottom = "20px";
                bar.$ext.style.height = "150px";

                document.body.style.marginBottom = "180px";

                tabs.on("ready", function(){
                    tabs.getPanes()[0].focus();
                    done();
                });
            });
            after(function(done){
                files.forEach(function(path){
                    fs.unlink(path, function(){
                        done();
                    });
                });
            });

            describe("save", function(){
                before(function(done){
                    var count = 0;
                    files.every(function(path, i){
                        count++;
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (--count === 0)
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });

                it('should save a tab that is changed', function(done) {
                    var path  = "/save1.txt";
                    var count = 0;

                    var c1 = function(){ count++; };

                    save.on("beforeSave", c1);
                    save.on("afterSave", c1);

                    var tab = changeTab(path, function(){
                        save.save(tab, null, function(err){
                            if (err) throw err;
                            expect(tab.document.changed).to.not.ok;

                            fs.readFile(path, function(err, data){
                                if (err) throw err;
                                expect(data).to.equal("test" + path);
                                expect(count).to.equal(2);
                                save.off("beforeSave", c1);
                                save.off("afterSave", c1);
                                done();
                            });
                        });
                    });
                });
                it('should queue saves when called sequentially', function(done) {
                    var tab = tabs.focussedTab;
                    var count = 0;
                    save.save(tab, null, function(err){
                        if (err) throw err;
                        expect(count).to.equal(0);
                        count++;
                    });
                    tab.editor.ace.insert("test");
                    save.save(tab, null, function(err){
                        if (err) throw err;
                        expect(count).to.equal(1);
                        done();
                    });
                });
                it('should save a tab at a new path/filename', function(done) {
                    var tab = changeTab("/save2.txt", function(){
                        var path = "/save2b.txt";
                        files.push(path); //cleanup

                        save.save(tab, { path: path }, function(err){
                            if (err) throw err;

                            expect(tab.path).to.equal(path);
                            expect(tab.document.changed).to.not.ok

                            fs.readFile(path, function(err, data){
                                if (err) throw err;
                                expect(data).to.equal("test/save2.txt");

                                fs.unlink(path, function(){
                                    tab.close();
                                    expect(tabs.getTabs().indexOf(tab)).to.equal(-1);
                                    done();
                                });
                            });
                        });
                    });
                });
                it('should show the saveAs dialog when saving a newfile without path in the options', function(done) {
                    var path = "/shouldnotsave.txt";
                    files.push(path); //cleanup

                    tabs.open({
                        active   : true,
                        path     : path,
                        document : {
                            value : "test",
                            meta  : {
                                newfile: true
                            }
                        }
                    }, function(err, tab){
                        save.save(tab, null, function(err){
                            expect(seen).to.ok;
                            tab.close();
                            done();
                        });
                    });

                    var seen = false;
                    setTimeout(function(){
                        var win = filesave.getElement("window");
                        var input = filesave.getElement("txtFilename");
                        expect(win.visible).to.ok;
                        expect(input.value).to.equal(path.substr(1));
                        seen = true;
                        win.hide();
                    }, TIMEOUT);
                });
                it('should not show the saveAs dialog when saving a newfile with path in the options', function(done) {
                    var path = "/shouldnotsave.txt";

                    tabs.open({
                        active   : true,
                        document : {
                            value : "test",
                            meta  : {
                                newfile: true
                            }
                        }
                    }, function(err, tab){
                        save.save(tab, { path: path}, function(err){
                            expect(tab.document.changed).not.ok
                            tab.close();
                            done();
                        });
                    });
                });
                it('should be triggered when closing a changed tab', function(done) {
                    var path = "/save3.txt";
                    var tab = changeTab(path, function(){
                        save.once("beforeWarn", function(){
                            setTimeout(function(){
                                save.once("afterSave", function(){
                                    fs.readFile(path, function(err, data){
                                        if (err) throw err;
                                        expect(data).to.equal("test" + path);
                                        done();
                                    });
                                });

                                question.getElement("yes").dispatchEvent("click");
                            }, 500)
                        });

                        tab.close();
                    });
                });
                it('should not be triggered when closing an unchanged tab', function(done) {
                    var path  = "/save1.txt";
                    var tab = tabs.findTab(path);
                    save.once("beforeWarn", function(){
                        throw new Error();
                    });
                    done();
                });
                it('should not be triggered when closing a new empty file', function(done) {
                    tabs.open({
                        active   : true,
                        document : {
                            value : "",
                            meta  : {
                                newfile: true
                            }
                        }
                    }, function(err, tab){
                        save.once("beforeWarn", function(){
                            throw new Error();
                        });

                        tab.close();
                        done();
                    });
                });
            });
            describe("saveAs", function(){
                before(function(done){
                    files.every(function(path, i){
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (path == files[2])
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should save a file under a new filename', function(done) {
                    var tab = tabs.focussedTab;
                    files.push("/save1b.txt");
                    fs.unlink("/save1b.txt", function(){
                        save.saveAs(tab, function(err){
                            expect(err).to.not.ok
                            expect(seen).to.ok
                            done();
                        });

                        var seen = false;
                        setTimeout(function(){
                            var win = filesave.getElement("window");
                            expect(win.visible).to.ok;
                            seen = true;
                            filesave.getElement("txtFilename").setValue("save1b.txt");
                            filesave.getElement("btnChoose").dispatchEvent("click");
                        }, TIMEOUT);
                    });
                });
                it('should trigger saveAs and then cancel it', function(done) {
                    var tab = tabs.focussedTab;
                    save.saveAs(tab, function(err){
                        expect(err).to.ok
                        expect(seen).to.ok
                        done();
                    });

                    var seen = false;
                    setTimeout(function(){
                        var win = filesave.getElement("window");
                        expect(win.visible).to.ok;
                        seen = true;
                        win.hide();
                    }, TIMEOUT);
                });
            });
            describe("revertToSaved", function(){
                before(function(done){
                    files.every(function(path, i){
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (path == files[2])
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should revert a change tab', function(done) {
                    var tab = changeTab("/save1.txt", function(){
                        save.revertToSaved(tab, function(err){
                            expect(err).to.not.ok;
                            setTimeout(function(){
                                expect(tab.document.changed).to.not.ok
                                expect(tab.document.value).to.equal("/save1.txt");
                                expect(tab.document.undoManager.length).to.equal(2);
                                expect(tab.document.undoManager.position).to.equal(1);
                                expect(tab.document.undoManager.isAtBookmark()).to.ok;
                                expect(tab.className.names.indexOf("loading")).to.equal(-1);
                                done();
                            }, TIMEOUT);
                        });
                    });
                });
            });
            describe("saveAll", function(){
                before(function(done){
                    var count = 0;

                    files.every(function(path, i){
                        count++;

                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (--count === 0)
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should save all changed files', function(done) {
                    var page3 = tabs.findTab("/save3.txt");
                    var page1 = changeTab("/save1.txt", function(){
                        var page2 = changeTab("/save2.txt", function(){
                            expect(page1.document.changed).to.ok;
                            expect(page2.document.changed).to.ok;
                            expect(page3.document.changed).to.not.ok;

                            save.saveAll(function(err){
                                if (err) throw err;

                                expect(page1.document.changed).to.not.ok;
                                expect(page2.document.changed).to.not.ok;
                                expect(page3.document.changed).to.not.ok;
                                done();
                            });
                        });
                    });
                });
            });
            describe("saveAllInteractive", function(){
                before(function(done){
                    files.every(function(path, i){
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (path == files[2])
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should be triggered when closing multiple pages that are changed', function(done) {
                    var page1 = changeTab("/save1.txt", function(){
                        var page2 = changeTab("/save2.txt", function(){
                            var page3 = changeTab("/save3.txt", function(){
                                var pages = [page1, page2, page3];

                                save.saveAllInteractive(pages, function(result){
                                    expect(result).to.equal(save.YESTOALL);
                                    done();
                                });

                                setTimeout(function(){
                                    question.getElement("yestoall").dispatchEvent("click");
                                }, TIMEOUT);
                            });
                        });
                    });
                });
            });
            if (!onload.remain){
                describe("unload()", function(){
                    it('should destroy all ui elements when it is unloaded', function(done) {
                        save.unload();
                        done();
                    });
                });

                //@todo Idea: show in the tabs whether the editor is running atm
                // @todo test fs integration

                after(function(done){
                    document.body.style.marginBottom = "";
                    tabs.unload();
                    done();
                });
            }
        });

        onload && onload();
    }
});<commit_msg>Add save as support and fixes issue of saving new file without an undo stack<commit_after>/*global describe it before after  =*/

"use client";

require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
  function (architect, chai, baseProc) {
    var expect = chai.expect;

    document.body.appendChild(document.createElement("div"))
        .setAttribute("id", "saveStatus");

    expect.setupArchitectTest([
        {
            packagePath : "plugins/c9.core/c9",
            startdate   : new Date(),
            debug       : true,
            hosted      : true,
            local       : false,
            davPrefix   : "/"
        },

        "plugins/c9.core/ext",
        "plugins/c9.core/http",
        "plugins/c9.core/util",
        "plugins/c9.ide.ui/lib_apf",
        "plugins/c9.ide.ui/menus",
        {
            packagePath: "plugins/c9.core/settings",
            testing: true
        },
        "plugins/c9.core/api.js",
        {
            packagePath  : "plugins/c9.ide.ui/ui",
            staticPrefix : "plugins/c9.ide.ui"
        },
        "plugins/c9.ide.editors/document",
        "plugins/c9.ide.editors/undomanager",
        {
            packagePath: "plugins/c9.ide.editors/editors",
            defaultEditor: "texteditor"
        },
        "plugins/c9.ide.editors/editor",
        "plugins/c9.ide.editors/tabmanager",
        "plugins/c9.ide.ui/focus",
        "plugins/c9.ide.editors/pane",
        "plugins/c9.ide.editors/tab",
        "plugins/c9.ide.ace/ace",
        "plugins/c9.ide.save/save",
        "plugins/c9.vfs.client/vfs_client",
        "plugins/c9.vfs.client/endpoint",
        "plugins/c9.ide.auth/auth",
        {
            packagePath: "plugins/c9.fs/fs",
            baseProc: baseProc
        },
        "plugins/c9.fs/fs.cache.xml",

        "plugins/c9.ide.dialog/dialog",
        "plugins/c9.ide.dialog.common/alert",
        "plugins/c9.ide.dialog.common/confirm",
        "plugins/c9.ide.dialog.common/filechange",
        "plugins/c9.ide.dialog.common/fileoverwrite",
        "plugins/c9.ide.dialog.common/fileremove",
        "plugins/c9.ide.dialog.common/question",
        "plugins/c9.ide.dialog.file/filesave",

        // Mock plugins
        {
            consumes : ["apf", "ui", "Plugin"],
            provides : [
                "commands", "menus", "commands", "layout", "watcher",
                "save", "anims", "tree", "preferences", "clipboard",
                "auth.bootstrap", "info", "dialog.error"
            ],
            setup    : expect.html.mocked
        },
        {
            consumes : [],
            provides : ["proc"],
            setup    : expect.html.mocked
        },
        {
            consumes : ["tabManager", "save", "fs", "dialog.filesave", "dialog.question"],
            provides : [],
            setup    : main
        }
    ], architect);

    function main(options, imports, register) {
        var tabs     = imports.tabManager;
        var fs       = imports.fs;
        var save     = imports.save;
        var filesave = imports["dialog.filesave"];
        var question = imports["dialog.question"];

        function countEvents(count, expected, done){
            if (count == expected)
                done();
            else
                throw new Error("Wrong Event Count: "
                    + count + " of " + expected);
        }

        expect.html.setConstructor(function(tab){
            if (typeof tab == "object")
                return tab.pane.aml.getPage("editor::" + tab.editorType).$ext;
        });

        function changeTab(path, done){
            var tab = tabs.findTab(path);
            if (!tab)
                return setTimeout(changeTab.bind(null, path, done), 100);

            tabs.focusTab(tab);
            tab.document.undoManager.once("change", function(){
                expect(tab.document.changed).to.ok;
                done();
            });
            tab.document.editor.ace.insert("test");

            return tab;
        }

        var TIMEOUT = 10;
        var files   = [];
        describe('save', function() {
            this.timeout(6000)

            before(function(done){
                apf.config.setProperty("allow-select", false);
                apf.config.setProperty("allow-blur", false);

                files = ["/save1.txt", "/save2.txt", "/save3.txt"];

                bar.$ext.style.background = "rgba(220, 220, 220, 0.93)";
                bar.$ext.style.position = "fixed";
                bar.$ext.style.left = "20px";
                bar.$ext.style.right = "20px";
                bar.$ext.style.bottom = "20px";
                bar.$ext.style.height = "150px";

                document.body.style.marginBottom = "180px";

                tabs.on("ready", function(){
                    tabs.getPanes()[0].focus();
                    done();
                });
            });
            after(function(done){
                files.forEach(function(path){
                    fs.unlink(path, function(){
                        done();
                    });
                });
            });

            describe("save", function(){
                before(function(done){
                    var count = 0;
                    files.every(function(path, i){
                        count++;
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (--count === 0)
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });

                it('should save a tab that is changed', function(done) {
                    var path  = "/save1.txt";
                    var count = 0;

                    var c1 = function(){ count++; };

                    save.on("beforeSave", c1);
                    save.on("afterSave", c1);

                    var tab = changeTab(path, function(){
                        save.save(tab, null, function(err){
                            if (err) throw err;
                            expect(tab.document.changed).to.not.ok;

                            fs.readFile(path, function(err, data){
                                if (err) throw err;
                                expect(data).to.equal("test" + path);
                                expect(count).to.equal(2);
                                save.off("beforeSave", c1);
                                save.off("afterSave", c1);
                                done();
                            });
                        });
                    });
                });
                it('should queue saves when called sequentially', function(done) {
                    var tab = tabs.focussedTab;
                    var count = 0;
                    save.save(tab, null, function(err){
                        if (err) throw err;
                        expect(count).to.equal(0);
                        count++;
                    });
                    tab.editor.ace.insert("test");
                    save.save(tab, null, function(err){
                        if (err) throw err;
                        expect(count).to.equal(1);
                        done();
                    });
                });
                it('should save a tab at a new path/filename', function(done) {
                    var tab = changeTab("/save2.txt", function(){
                        var path = "/save2b.txt";
                        files.push(path); //cleanup

                        save.save(tab, { path: path }, function(err){
                            if (err) throw err;

                            expect(tab.path).to.equal(path);
                            expect(tab.document.changed).to.not.ok

                            fs.readFile(path, function(err, data){
                                if (err) throw err;
                                expect(data).to.equal("test/save2.txt");

                                fs.unlink(path, function(){
                                    tab.close();
                                    expect(tabs.getTabs().indexOf(tab)).to.equal(-1);
                                    done();
                                });
                            });
                        });
                    });
                });
                it('should show the saveAs dialog when saving a newfile without path in the options', function(done) {
                    var path = "/shouldnotsave.txt";
                    files.push(path); //cleanup

                    tabs.open({
                        active   : true,
                        path     : path,
                        document : {
                            value : "test",
                            meta  : {
                                newfile: true
                            }
                        }
                    }, function(err, tab){
                        save.save(tab, null, function(err){
                            expect(seen).to.ok;
                            tab.close();
                            done();
                        });
                    });

                    var seen = false;
                    setTimeout(function(){
                        var win = filesave.getElement("window");
                        var input = filesave.getElement("txtFilename");
                        expect(win.visible).to.ok;
                        expect(input.value).to.equal(path.substr(1));
                        seen = true;
                        win.hide();
                    }, TIMEOUT);
                });
                it.only('should not show the saveAs dialog when saving a newfile with path in the options', function(done) {
                    var path = "/shouldnotsave.txt";

                    tabs.open({
                        active   : true,
                        document : {
                            value : "test",
                            meta  : {
                                newfile: true
                            }
                        }
                    }, function(err, tab){
                        save.save(tab, { path: path}, function(err){
                            expect(tab.document.changed).not.ok;
                            debugger;
                            expect(tab.document.meta.newfile).not.ok;
                            tab.close();
                            done();
                        });
                    });
                });
                it('should be triggered when closing a changed tab', function(done) {
                    var path = "/save3.txt";
                    var tab = changeTab(path, function(){
                        save.once("beforeWarn", function(){
                            setTimeout(function(){
                                save.once("afterSave", function(){
                                    fs.readFile(path, function(err, data){
                                        if (err) throw err;
                                        expect(data).to.equal("test" + path);
                                        done();
                                    });
                                });

                                question.getElement("yes").dispatchEvent("click");
                            }, 500)
                        });

                        tab.close();
                    });
                });
                it('should not be triggered when closing an unchanged tab', function(done) {
                    var path  = "/save1.txt";
                    var tab = tabs.findTab(path);
                    save.once("beforeWarn", function(){
                        throw new Error();
                    });
                    done();
                });
                it('should not be triggered when closing a new empty file', function(done) {
                    tabs.open({
                        active   : true,
                        document : {
                            value : "",
                            meta  : {
                                newfile: true
                            }
                        }
                    }, function(err, tab){
                        save.once("beforeWarn", function(){
                            throw new Error();
                        });

                        tab.close();
                        done();
                    });
                });
            });
            describe("saveAs", function(){
                before(function(done){
                    files.every(function(path, i){
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (path == files[2])
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should save a file under a new filename', function(done) {
                    var tab = tabs.focussedTab;
                    files.push("/save1b.txt");
                    fs.unlink("/save1b.txt", function(){
                        save.saveAs(tab, function(err){
                            expect(err).to.not.ok
                            expect(seen).to.ok
                            done();
                        });

                        var seen = false;
                        setTimeout(function(){
                            var win = filesave.getElement("window");
                            expect(win.visible).to.ok;
                            seen = true;
                            filesave.getElement("txtFilename").setValue("save1b.txt");
                            filesave.getElement("btnChoose").dispatchEvent("click");
                        }, TIMEOUT);
                    });
                });
                it('should trigger saveAs and then cancel it', function(done) {
                    var tab = tabs.focussedTab;
                    save.saveAs(tab, function(err){
                        expect(err).to.ok
                        expect(seen).to.ok
                        done();
                    });

                    var seen = false;
                    setTimeout(function(){
                        var win = filesave.getElement("window");
                        expect(win.visible).to.ok;
                        seen = true;
                        win.hide();
                    }, TIMEOUT);
                });
            });
            describe("revertToSaved", function(){
                before(function(done){
                    files.every(function(path, i){
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (path == files[2])
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should revert a change tab', function(done) {
                    var tab = changeTab("/save1.txt", function(){
                        save.revertToSaved(tab, function(err){
                            expect(err).to.not.ok;
                            setTimeout(function(){
                                expect(tab.document.changed).to.not.ok
                                expect(tab.document.value).to.equal("/save1.txt");
                                expect(tab.document.undoManager.length).to.equal(2);
                                expect(tab.document.undoManager.position).to.equal(1);
                                expect(tab.document.undoManager.isAtBookmark()).to.ok;
                                expect(tab.className.names.indexOf("loading")).to.equal(-1);
                                done();
                            }, TIMEOUT);
                        });
                    });
                });
            });
            describe("saveAll", function(){
                before(function(done){
                    var count = 0;

                    files.every(function(path, i){
                        count++;

                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (--count === 0)
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should save all changed files', function(done) {
                    var page3 = tabs.findTab("/save3.txt");
                    var page1 = changeTab("/save1.txt", function(){
                        var page2 = changeTab("/save2.txt", function(){
                            expect(page1.document.changed).to.ok;
                            expect(page2.document.changed).to.ok;
                            expect(page3.document.changed).to.not.ok;

                            save.saveAll(function(err){
                                if (err) throw err;

                                expect(page1.document.changed).to.not.ok;
                                expect(page2.document.changed).to.not.ok;
                                expect(page3.document.changed).to.not.ok;
                                done();
                            });
                        });
                    });
                });
            });
            describe("saveAllInteractive", function(){
                before(function(done){
                    files.every(function(path, i){
                        fs.writeFile(path, path, function(){
                            tabs.openFile(path, function(){
                                if (path == files[2])
                                    done();
                            });
                        });
                        return i == 2 ? false : true;
                    });
                });
                after(function(done){
                    tabs.getTabs().forEach(function(tab){
                        tab.unload();
                    });
                    done();
                });

                it('should be triggered when closing multiple pages that are changed', function(done) {
                    var page1 = changeTab("/save1.txt", function(){
                        var page2 = changeTab("/save2.txt", function(){
                            var page3 = changeTab("/save3.txt", function(){
                                var pages = [page1, page2, page3];

                                save.saveAllInteractive(pages, function(result){
                                    expect(result).to.equal(save.YESTOALL);
                                    done();
                                });

                                setTimeout(function(){
                                    question.getElement("yestoall").dispatchEvent("click");
                                }, TIMEOUT);
                            });
                        });
                    });
                });
            });
            if (!onload.remain){
                describe("unload()", function(){
                    it('should destroy all ui elements when it is unloaded', function(done) {
                        save.unload();
                        done();
                    });
                });

                //@todo Idea: show in the tabs whether the editor is running atm
                // @todo test fs integration

                after(function(done){
                    document.body.style.marginBottom = "";
                    tabs.unload();
                    done();
                });
            }
        });

        onload && onload();
    }
});<|endoftext|>


<commit_before>//var request = require('request-promise');
//var Promise = require('bluebird');

function basic(){
    this.develop = true;

    this.preferredName = "basicAI";

    this.play = function(hand, currentCard, draw, play){
        console.log(hand);
        console.log(currentCard);
        /*
            Special cards of the same color
                draw 2
                skip
                reverse
            Any card of the same color
            Any card of the same number (preferring the color that is has the most of)
            A Draw 4, choosing the color it has the most of
            A Wild, choosing the color it has the most of
            Draw
        */

    };

    return this;
}

module.exports = basic;
<commit_msg>Implement basic logic for ai<commit_after>//var request = require('request-promise');
//var Promise = require('bluebird');

function basic(){
    this.develop = true;

    this.preferredName = "basicAI";

    this.play = function(hand, currentCard, draw, play){
        console.log(hand);
        console.log(currentCard);
        /*
            Special cards of the same color
                draw 2
                skip
                reverse
            Any card of the same color
            Any card of the same number (preferring the color that is has the most of)
            A Draw 4, choosing the color it has the most of
            A Wild, choosing the color it has the most of
            Draw
        */

        //Play card of the same color
        var handColors = hand.filter(function(item){ return item.color === currentCard.color; });

        if (handColors.length > 0){
            if (handColors.filter(function(i) { return i.value === 'draw 2'; }).length > 0){
                play(currentCard.color, 'draw 2');
                return;
            }

            if (handColors.filter(function(i) { return i.value === 'skip'; }).length > 0){
                play(currentCard.color, 'skip');
                return;
            }

            if (handColors.filter(function(i) { return i.value === 'reverse'; }).length > 0){
                play(currentCard.color, 'reverse');
                return;
            }

            play(currentCard.color, handColors[0]);
        }

        var handValues = hand.filter(function(item){ return item.value === currentCard.value; });

        //play card of the same value, of the color we have the most of
        if (handValues.length > 0){
            var bestColor = {
                color: '',
                count: 0
            };

            for (var i = 0; i < handValues.length; i++){
                var count = hand.filter(function(i){ return i.color === handValues[i].color }).length;
                if (count > bestColor.count){
                    bestColor.color = handValues[i].color;
                    bestColor.count = count;
                }
            }

            play(bestColor.color, currentCard.value);
            return;
        }

        var handWilds = hand.filter(function(item){ return item.color === 'wild'; });

        //Play wilds
        if (handWilds > 0){
            var bestColor = {
                color: '',
                count: 0
            };

            for (var i = 0; i < hand.length; i++){
                var count = hand.filter(function(i){ return i.color === hand[i].color }).length;
                if (count > bestColor.count){
                    bestColor.color = hand[i].color;
                    bestColor.count = count;
                }
            }

            if (handWilds.filter(function(i){ return i.value === 'draw 4'; }).length > 0){
                play(bestColor.color, 'draw 4');
                return;
            }

            play(bestColor.color, 'draw');
            return;
        }

        draw();
    };

    return this;
}

module.exports = basic;
<|endoftext|>


<commit_before>var win2 = Ti.UI.currentWindow;

var view1 = Ti.UI.createView({
	left:10,
	height:50,
	width:100
});
var view2 = Ti.UI.createView({
	left:40,
	height:200,
	width:400,
	backgroundColor:'#212121',
	visible:false
});

var label3 = Titanium.UI.createLabel({
	color:'#999',
	text:'PROBLEEM',
	font:{fontSize:20,fontFamily:'Helvetica Neue'},
	textAlign:'center',
	width:'auto'
});

var buttonTestView = Titanium.UI.createButton({
	title:'Volgende stap',
	top:10,
	right:10
});

var button2 = Titanium.UI.createButton({
	title:'Nog volgendere stap',
	top:10,
	left:10
});

view1.add(buttonTestView);
view2.add(label3);
view2.add(button2);
win2.add(view1);
win2.add(view2);

buttonTestView.addEventListener('click',function(e)
{
	//window openen
	view1.hide();
	view2.show();
});

button2.addEventListener('click',function(e)
{
	//window openen
	view2.hide();
	view1.show();
});
<commit_msg>test2<commit_after>var win2 = Ti.UI.currentWindow;

var view1 = Ti.UI.createView({
	left:10,
	height:50,
	width:100
});
var view2 = Ti.UI.createView({
	left:40,
	height:200,
	width:400,
	backgroundColor:'#212121',
	visible:false
});

var label3 = Titanium.UI.createLabel({
	color:'#999',
	text:'PROBLEEM',
	font:{fontSize:20,fontFamily:'Helvetica Neue'},
	textAlign:'center',
	width:'auto'
});

var buttonTestView = Titanium.UI.createButton({
	title:'Volgende stap',
	top:10,
	right:10
});

var button2 = Titanium.UI.createButton({
	title:'Vorige stap',
	top:10,
	left:10
});

view1.add(buttonTestView);
view2.add(label3);
view2.add(button2);
win2.add(view1);
win2.add(view2);

buttonTestView.addEventListener('click',function(e)
{
	//window openen
	view1.hide();
	view2.show();
});

button2.addEventListener('click',function(e)
{
	//window openen
	view2.hide();
	view1.show();
});
<|endoftext|>


<commit_before>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack bot built with Botkit.

This bot demonstrates many of the core features of Botkit:

* Connect to Slack using the real time API
* Receive messages based on "spoken" patterns
* Reply to messages
* Use the conversation system to ask questions
* Use the built in storage system to store and retrieve information
  for a user.

# RUN THE BOT:

  Get a Bot token from Slack:

    -> http://my.slack.com/services/new/bot

  Run your bot from the command line:

    token=<MY TOKEN> node slack_bot.js

# USE THE BOT:

  Find your bot inside Slack to send it a direct message.

  Say: "Hello"

  The bot will reply "Hello!"

  Say: "who are you?"

  The bot will tell you its name, where it is running, and for how long.

  Say: "Call me <nickname>"

  Tell the bot your nickname. Now you are friends.

  Say: "who am I?"

  The bot will tell you your nickname, if it knows one for you.

  Say: "shutdown"

  The bot will ask if you are sure, and then shut itself down.

  Make sure to invite your bot into other channels using /invite @<my bot>!

# EXTEND THE BOT:

  Botkit has many features for building cool and useful bots!

  Read all about it here:

    -> http://howdy.ai/botkit

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true
});

var bot = controller.spawn({
    token: process.env.token
}).startRTM();

controller.hears(['こんにちは', 'やあ', 'どうも', 'Hello', 'hi'], 'direct_message,direct_mention,mention', function(bot, message) {

    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'cat',
    }, function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(', err);
        }
    });

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'こんにちは ' + user.name + 'さん!!');
        } else {
            bot.reply(message, 'こんにちは！');
        }
    });
});

controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {
    var name = message.match[1];
    controller.storage.users.get(message.user, function(err, user) {
        if (!user) {
            user = {
                id: message.user,
            };
        }
        user.name = name;
        controller.storage.users.save(user, function(err, id) {
            bot.reply(message, 'わかったー！ あなたのことは ' + user.name + ' さんって呼ぶね！');
        });
    });
});

controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention', function(bot, message) {

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'あなたは ' + user.name + 'さんだよね！');
        } else {
            bot.startConversation(message, function(err, convo) {
                if (!err) {
                    convo.say('まだあなたの名前をしらないよ！');
                    convo.ask('なんて呼んでほしい？', function(response, convo) {
                        convo.ask('あなたは `' + response.text + '`って呼んでほしいの？（y/n）', [
                            {
                              if(response.text == 'y') {
                                callback: function(response, convo) {
                                    // since no further messages are queued after this,
                                    // the conversation will end naturally with status == 'completed'
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'n',
                                callback: function(response, convo) {
                                    // stop the conversation. this will cause it to end with status == 'stopped'
                                    convo.stop();
                                }
                            },
                            {
                                default: true,
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            }
                        ]);

                        convo.next();

                    }, {'key': 'nickname'}); // store the results in a field called nickname

                    convo.on('end', function(convo) {
                        if (convo.status == 'completed') {
                            bot.reply(message, 'OK! I will update my dossier...');

                            controller.storage.users.get(message.user, function(err, user) {
                                if (!user) {
                                    user = {
                                        id: message.user,
                                    };
                                }
                                user.name = convo.extractResponse('nickname');
                                controller.storage.users.save(user, function(err, id) {
                                    bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
                                });
                            });

                        } else {
                            // this happens if the conversation ended prematurely for some reason
                            bot.reply(message, 'はーい！おぼえたよ！');
                        }
                    });
                }
            });
        }
    });
});

controller.hears(['こんばんは', '残業','こんばん'], function(bot,message) {
//    console.log(message);
    askNight = function(response, convo) {
//        console.log(response);
        convo.ask('こんばんは、'+ user.name + 'さん！ 今日は遅いの？', function(response, convo) {
//            console.log(response);
            convo.say('おつかれさま！');
            if(response.text == ['y','yes','はい', 'うん']) {
                askZangyo(response, convo);
            }
            convo.next();
        });
    };

    askZangyo = function(response, convo) {
        convo.ask('おなかすいた？', function(response, convo) {
            if(response.text == ['y','yes','はい', 'うん']) {
                convo.say('あげる！　http://sheba.jp/product/duo/');
            } else {
                convo.say('そっか〜、、がんばってね〜');
            }
            convo.next();
        });
    };

    bot.startConversation(message, askNight);
});

/*controller.hears(['shutdown'], 'direct_message,direct_mention,mention', function(bot, message) {

    bot.startConversation(message, function(err, convo) {

        convo.ask('Are you sure you want me to shutdown?', [
            {
                pattern: bot.utterances.yes,
                callback: function(response, convo) {
                    convo.say('Bye!');
                    convo.next();
                    setTimeout(function() {
                        process.exit();
                    }, 3000);
                }
            },
        {
            pattern: bot.utterances.no,
            default: true,
            callback: function(response, convo) {
                convo.say('*Phew!*');
                convo.next();
            }
        }
        ]);
    });
});*/

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

    });

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<commit_msg>[Change]旧戻し<commit_after>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack bot built with Botkit.

This bot demonstrates many of the core features of Botkit:

* Connect to Slack using the real time API
* Receive messages based on "spoken" patterns
* Reply to messages
* Use the conversation system to ask questions
* Use the built in storage system to store and retrieve information
  for a user.

# RUN THE BOT:

  Get a Bot token from Slack:

    -> http://my.slack.com/services/new/bot

  Run your bot from the command line:

    token=<MY TOKEN> node slack_bot.js

# USE THE BOT:

  Find your bot inside Slack to send it a direct message.

  Say: "Hello"

  The bot will reply "Hello!"

  Say: "who are you?"

  The bot will tell you its name, where it is running, and for how long.

  Say: "Call me <nickname>"

  Tell the bot your nickname. Now you are friends.

  Say: "who am I?"

  The bot will tell you your nickname, if it knows one for you.

  Say: "shutdown"

  The bot will ask if you are sure, and then shut itself down.

  Make sure to invite your bot into other channels using /invite @<my bot>!

# EXTEND THE BOT:

  Botkit has many features for building cool and useful bots!

  Read all about it here:

    -> http://howdy.ai/botkit

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true
});

var bot = controller.spawn({
    token: process.env.token
}).startRTM();

controller.hears(['こんにちは', 'やあ', 'どうも', 'Hello', 'hi'], 'direct_message,direct_mention,mention', function(bot, message) {

    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'cat',
    }, function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(', err);
        }
    });

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'こんにちは ' + user.name + 'さん!!');
        } else {
            bot.reply(message, 'こんにちは！');
        }
    });
});

controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {
    var name = message.match[1];
    controller.storage.users.get(message.user, function(err, user) {
        if (!user) {
            user = {
                id: message.user,
            };
        }
        user.name = name;
        controller.storage.users.save(user, function(err, id) {
            bot.reply(message, 'わかったー！ あなたのことは ' + user.name + ' さんって呼ぶね！');
        });
    });
});

controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention', function(bot, message) {

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'あなたは ' + user.name + 'さんだよね！');
        } else {
            bot.startConversation(message, function(err, convo) {
                if (!err) {
                    convo.say('まだあなたの名前をしらないよ！');
                    convo.ask('なんて呼んでほしい？', function(response, convo) {
                        convo.ask('あなたは `' + response.text + '`って呼んでほしいの？（y/n）', [
                            {
                              if(response.text == 'y') {
                                callback: function(response, convo) {
                                    // since no further messages are queued after this,
                                    // the conversation will end naturally with status == 'completed'
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'n',
                                callback: function(response, convo) {
                                    // stop the conversation. this will cause it to end with status == 'stopped'
                                    convo.stop();
                                }
                            },
                            {
                                default: true,
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            }
                        ]);

                        convo.next();

                    }, {'key': 'nickname'}); // store the results in a field called nickname

                    convo.on('end', function(convo) {
                        if (convo.status == 'completed') {
                            bot.reply(message, 'OK! I will update my dossier...');

                            controller.storage.users.get(message.user, function(err, user) {
                                if (!user) {
                                    user = {
                                        id: message.user,
                                    };
                                }
                                user.name = convo.extractResponse('nickname');
                                controller.storage.users.save(user, function(err, id) {
                                    bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
                                });
                            });

                        } else {
                            // this happens if the conversation ended prematurely for some reason
                            bot.reply(message, 'はーい！おぼえたよ！');
                        }
                    });
                }
            });
        }
    });
});

/*controller.hears(['shutdown'], 'direct_message,direct_mention,mention', function(bot, message) {

    bot.startConversation(message, function(err, convo) {

        convo.ask('Are you sure you want me to shutdown?', [
            {
                pattern: bot.utterances.yes,
                callback: function(response, convo) {
                    convo.say('Bye!');
                    convo.next();
                    setTimeout(function() {
                        process.exit();
                    }, 3000);
                }
            },
        {
            pattern: bot.utterances.no,
            default: true,
            callback: function(response, convo) {
                convo.say('*Phew!*');
                convo.next();
            }
        }
        ]);
    });
});*/

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

    });

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<|endoftext|>


<commit_before>'use strict';

module.exports = function(kbox) {

  // Node
  var path = require('path');
  var os = require('os');

  // NPM
  var _ = require('lodash');
  var fs = require('fs-extra');

  // Kalabox
  var Promise = kbox.Promise;
  var events = kbox.core.events.context();
  var share = require('./share.js')(kbox);

  // Syncthing container
  var syncthingContainer = function() {
    return {
      compose: [path.resolve(__dirname, '..', 'kalabox-compose.yml')],
      project: 'kalabox',
      opts: {
        services: ['syncthing'],
        internal: true
      }
    };
  };

  /*
   * Mix in our with default syncthing config and set into the app
   */
  events.on('post-app-create', function(app) {

    // Our default syncthing configuration
    var defaultConfig = kbox.core.config.normalize({
      codeDir: 'code',
      codeRoot: path.join(app.root, ':codeDir:'),
      ignore: [
        '*.7z',
        '*.bz2',
        '*.dmg',
        '*.gz',
        '*.iso',
        '*.jar',
        '*.rar',
        '*.tar',
        '*.tgz',
        '*.un~',
        '*.zip',
        '.*.swp',
        '.*DS_Store',
        '.DS_Store*',
        '._*',
        '.sass-cache',
        'Icon',
        'Thumbs.db',
        'ehthumbs.db'
      ]
    });

    // Mix in our user config if we have it
    var config = defaultConfig;
    if (app.config.pluginconfig.syncthing) {
      config = _.merge(config, app.config.pluginconfig.syncthing);
    }

    // Set the official syncthning config and remove the old one
    app.config.syncthing = config;
    delete app.config.pluginconfig.syncthing;

  });

  /*
   * Turn off the local sycnthing when the engine goes down
   */
  events.on('pre-engine-down', function() {
    // Get local sync instance
    return share.getLocalSync()
    .then(function(localSync) {
      // Check if it's up
      return localSync.isUp()
      .then(function(isUp) {
        if (isUp) {
          // If it's up, then shut'er down.
          return localSync.shutdown();
        }
      });
    });

  });

  /*
   * App events
   */
  kbox.whenAppRegistered(function(app) {

    /*
     * Restart our shares
     */
    events.on('pre-app-stop', function() {
      if (app.config.syncthing.share) {
        return share.restart();
      }
    });

    /*
     * Restart our shares
     * When we start an app make sure we mount the share to the correct
     * containers webroot
     */
    events.on('pre-app-start', 1, function(app) {

      if (app.config.syncthing.share) {

        // Get the host code root
        var codeRoot = app.config.syncthing.codeRoot;

        // Make sure our code root exist
        return Promise.fromNode(function(cb) {
          fs.mkdirp(codeRoot, cb);
        })

        // Write the local ignore file
        // for
        .then(function() {
          var shareIgnores = app.config.syncthing.ignore.join(os.EOL);
          var stignoreFile = path.join(codeRoot, '.stignore');
          return Promise.fromNode(function(cb) {
            fs.writeFile(stignoreFile, shareIgnores, cb);
          });
        })

        // Refresh our syncthing situation
        .then(function() {
          return share.restart();
        })

        // Make sure remote dir exists
        .then(function() {
          // Command to make query.
          var createCmd = [
            '-p',
            '/code/' + app.name
          ];
          // Build run definition
          var createDef = syncthingContainer();
          createDef.opts.entrypoint = '/bin/mkdir';
          createDef.opts.cmd = createCmd;
          return kbox.engine.run(createDef);
        })

        // Make sure it has a .stfolder file
        .then(function() {
          // Command to make query.
          var touchCmd = ['/code/' + app.name + '/.stfolder'];
          // Build run definition
          var touchDef = syncthingContainer();
          touchDef.opts.entrypoint = 'touch';
          touchDef.opts.cmd = touchCmd;
          return kbox.engine.run(touchDef);
        })

        // Write the remote ignore file
        .then(function() {
          // Compute the remote ignore file location
          var home = kbox.core.deps.get('globalConfig').home;
          var remoteDir = _.trimLeft(app.config.syncthing.codeRoot, home);
          // Command to make query.
          var cpCmd = [
            '/src/' + remoteDir + '/.stignore',
            '/code/' + app.name + '/.stignore'
          ];
          // Build run definition
          var runDef = syncthingContainer();
          runDef.opts.entrypoint = '/bin/cp';
          runDef.opts.cmd = cpCmd;
          return kbox.engine.run(runDef);
        })

        // Add the sharing mount to container with the webroot at that webroot
        .then(function() {

          // Inspect syncthing
          return kbox.engine.inspect(syncthingContainer())

          // Return syncthing code mount.
          .then(function(data) {
            return _.result(_.find(data.Mounts, function(mount) {
              return mount.Destination === '/code';
            }), 'Source');
          })

          // Create an override kalabox-compose yaml that contains
          // the volumezzz
          .then(function(syncMount) {

            // Get our stuff from the sync config
            var parts = app.config.syncthing.share.split(':');
            var webService = parts[0];
            var webRoot = parts[1];

            // Create dir to store this stuff
            var tmpDir = path.join(kbox.util.disk.getTempDir(), app.name);
            fs.mkdirpSync(tmpDir);

            // Start them up
            var currentCompose = {};
            var newCompose = {};

            // Get our composed things
            _.forEach(app.composeCore, function(file)  {
              _.extend(currentCompose, kbox.util.yaml.toJson(file));
            });

            // Add sync to correct container
            _.forEach(currentCompose, function(value, key)  {
              if (key === webService) {
                var vol = syncMount + '/' + app.name + ':' + webRoot;
                if (Array.isArray(value.volumes)) {
                  value.volumes.push(vol);
                }
                else {
                  value.volumes = [vol];
                }
                var obj = {};
                obj[key] = {volumes: _.uniq(value.volumes)};
                _.extend(newCompose, obj);
              }
            });

            if (!_.isEmpty(newCompose)) {
              var fileName = [app.name, _.uniqueId()].join('-');
              var newComposeFile = path.join(tmpDir, fileName + '.yml');
              kbox.util.yaml.toYamlFile(newCompose, newComposeFile);
              app.composeCore.push(newComposeFile);
            }

          });
        });
      }
    });

    /*
     * Clear app folder when the app is uninstalled.
     */
    events.on('post-app-uninstall', function(app) {

      // Array the syncthing instances
      var syncs = [share.getRemoteSync(), share.getLocalSync()];

      // Remove our folders
      return Promise.each(syncs, function(sync) {
        return sync.isUp()
        .then(function(isUp) {
          if (isUp) {
            return sync.clearFolder(app.name)
            .then(function() {
              return sync.restartWait();
            });
          }
        });
      });

    });

    /*
     * Delete the code volume on a destroy
     */
    events.on('post-app-destroy', function(app) {

      // Command to remove
      var rmCmd = [
        '-rf',
        '/code/' + app.name
      ];

      // Build kill definition
      var rmDef = syncthingContainer();
      rmDef.opts.entrypoint = 'rm';
      rmDef.opts.cmd = rmCmd;

      // Run the kill
      return kbox.engine.run(rmDef);

    });

  });

};
<commit_msg>#880: Do array manipulaton vs trimLeft to get syncthing remotedir<commit_after>'use strict';

module.exports = function(kbox) {

  // Node
  var path = require('path');
  var os = require('os');

  // NPM
  var _ = require('lodash');
  var fs = require('fs-extra');

  // Kalabox
  var Promise = kbox.Promise;
  var events = kbox.core.events.context();
  var share = require('./share.js')(kbox);

  // Syncthing container
  var syncthingContainer = function() {
    return {
      compose: [path.resolve(__dirname, '..', 'kalabox-compose.yml')],
      project: 'kalabox',
      opts: {
        services: ['syncthing'],
        internal: true
      }
    };
  };

  /*
   * Mix in our with default syncthing config and set into the app
   */
  events.on('post-app-create', function(app) {

    // Our default syncthing configuration
    var defaultConfig = kbox.core.config.normalize({
      codeDir: 'code',
      codeRoot: path.join(app.root, ':codeDir:'),
      ignore: [
        '*.7z',
        '*.bz2',
        '*.dmg',
        '*.gz',
        '*.iso',
        '*.jar',
        '*.rar',
        '*.tar',
        '*.tgz',
        '*.un~',
        '*.zip',
        '.*.swp',
        '.*DS_Store',
        '.DS_Store*',
        '._*',
        '.sass-cache',
        'Icon',
        'Thumbs.db',
        'ehthumbs.db'
      ]
    });

    // Mix in our user config if we have it
    var config = defaultConfig;
    if (app.config.pluginconfig.syncthing) {
      config = _.merge(config, app.config.pluginconfig.syncthing);
    }

    // Set the official syncthning config and remove the old one
    app.config.syncthing = config;
    delete app.config.pluginconfig.syncthing;

  });

  /*
   * Turn off the local sycnthing when the engine goes down
   */
  events.on('pre-engine-down', function() {
    // Get local sync instance
    return share.getLocalSync()
    .then(function(localSync) {
      // Check if it's up
      return localSync.isUp()
      .then(function(isUp) {
        if (isUp) {
          // If it's up, then shut'er down.
          return localSync.shutdown();
        }
      });
    });

  });

  /*
   * App events
   */
  kbox.whenAppRegistered(function(app) {

    /*
     * Restart our shares
     */
    events.on('pre-app-stop', function() {
      if (app.config.syncthing.share) {
        return share.restart();
      }
    });

    /*
     * Restart our shares
     * When we start an app make sure we mount the share to the correct
     * containers webroot
     */
    events.on('pre-app-start', 1, function(app) {

      if (app.config.syncthing.share) {

        // Get the host code root
        var codeRoot = app.config.syncthing.codeRoot;

        // Make sure our code root exist
        return Promise.fromNode(function(cb) {
          fs.mkdirp(codeRoot, cb);
        })

        // Write the local ignore file
        // for
        .then(function() {
          var shareIgnores = app.config.syncthing.ignore.join(os.EOL);
          var stignoreFile = path.join(codeRoot, '.stignore');
          return Promise.fromNode(function(cb) {
            fs.writeFile(stignoreFile, shareIgnores, cb);
          });
        })

        // Refresh our syncthing situation
        .then(function() {
          return share.restart();
        })

        // Make sure remote dir exists
        .then(function() {
          // Command to make query.
          var createCmd = [
            '-p',
            '/code/' + app.name
          ];
          // Build run definition
          var createDef = syncthingContainer();
          createDef.opts.entrypoint = '/bin/mkdir';
          createDef.opts.cmd = createCmd;
          return kbox.engine.run(createDef);
        })

        // Make sure it has a .stfolder file
        .then(function() {
          // Command to make query.
          var touchCmd = ['/code/' + app.name + '/.stfolder'];
          // Build run definition
          var touchDef = syncthingContainer();
          touchDef.opts.entrypoint = 'touch';
          touchDef.opts.cmd = touchCmd;
          return kbox.engine.run(touchDef);
        })

        // Write the remote ignore file
        .then(function() {

          // Compute the remote ignore file location
          var home = kbox.core.deps.get('globalConfig').home;
          var homeSplit = home.split(path.sep);
          var codeRootSplit = app.config.syncthing.codeRoot.split(path.sep);
          var remoteDir = _.drop(codeRootSplit, homeSplit.length).join('/');

          // Command to make query.
          var cpCmd = [
            '/src/' + remoteDir + '/.stignore',
            '/code/' + app.name + '/.stignore'
          ];
          // Build run definition
          var runDef = syncthingContainer();
          runDef.opts.entrypoint = '/bin/cp';
          runDef.opts.cmd = cpCmd;
          return kbox.engine.run(runDef);
        })

        // Add the sharing mount to container with the webroot at that webroot
        .then(function() {

          // Inspect syncthing
          return kbox.engine.inspect(syncthingContainer())

          // Return syncthing code mount.
          .then(function(data) {
            return _.result(_.find(data.Mounts, function(mount) {
              return mount.Destination === '/code';
            }), 'Source');
          })

          // Create an override kalabox-compose yaml that contains
          // the volumezzz
          .then(function(syncMount) {

            // Get our stuff from the sync config
            var parts = app.config.syncthing.share.split(':');
            var webService = parts[0];
            var webRoot = parts[1];

            // Create dir to store this stuff
            var tmpDir = path.join(kbox.util.disk.getTempDir(), app.name);
            fs.mkdirpSync(tmpDir);

            // Start them up
            var currentCompose = {};
            var newCompose = {};

            // Get our composed things
            _.forEach(app.composeCore, function(file)  {
              _.extend(currentCompose, kbox.util.yaml.toJson(file));
            });

            // Add sync to correct container
            _.forEach(currentCompose, function(value, key)  {
              if (key === webService) {
                var vol = syncMount + '/' + app.name + ':' + webRoot;
                if (Array.isArray(value.volumes)) {
                  value.volumes.push(vol);
                }
                else {
                  value.volumes = [vol];
                }
                var obj = {};
                obj[key] = {volumes: _.uniq(value.volumes)};
                _.extend(newCompose, obj);
              }
            });

            if (!_.isEmpty(newCompose)) {
              var fileName = [app.name, _.uniqueId()].join('-');
              var newComposeFile = path.join(tmpDir, fileName + '.yml');
              kbox.util.yaml.toYamlFile(newCompose, newComposeFile);
              app.composeCore.push(newComposeFile);
            }

          });
        });
      }
    });

    /*
     * Clear app folder when the app is uninstalled.
     */
    events.on('post-app-uninstall', function(app) {

      // Array the syncthing instances
      var syncs = [share.getRemoteSync(), share.getLocalSync()];

      // Remove our folders
      return Promise.each(syncs, function(sync) {
        return sync.isUp()
        .then(function(isUp) {
          if (isUp) {
            return sync.clearFolder(app.name)
            .then(function() {
              return sync.restartWait();
            });
          }
        });
      });

    });

    /*
     * Delete the code volume on a destroy
     */
    events.on('post-app-destroy', function(app) {

      // Command to remove
      var rmCmd = [
        '-rf',
        '/code/' + app.name
      ];

      // Build kill definition
      var rmDef = syncthingContainer();
      rmDef.opts.entrypoint = 'rm';
      rmDef.opts.cmd = rmCmd;

      // Run the kill
      return kbox.engine.run(rmDef);

    });

  });

};
<|endoftext|>


<commit_before>#!/usr/bin/env node
/*
 * Tool to crawl a Reveal.js slide deck and capture each fragment to build a PDF.
 * Author: Katie Miller (codemiller)
 */

var fs = require('fs');
var path = require('path');
var build = require('./../build.js');
var capture = require('./../capture.js');
var resolutionPattern = /(\d+)x(\d+)/;

var argv = require('yargs')
      .options('t', {
        alias: 'target',
        default: 'output',
        describe: 'Directory to write the images and PDF'
      })
      .options('o', {
        alias: 'output',
        default: 'presentation',
        describe: 'Filename for the PDF (without an extension)'
      })
      .options('resolution', {
        describe: 'Resolution for viewport; if unspecified, tool will use the Reveal.js slide dimensions'
      })
      .options('format', {
        default: 'jpeg',
        describe: 'Format of images captured'
      })
      .options('controls', {
        default: false,
        boolean: true
      })
      .options('wait', {
        alias: 'wait-time',
        default: 1000,
        describe: 'Number of milliseconds to wait for each slide/fragment to load before capture'
      })
      .options('quality', {
        default: 100,
        describe: 'Quality of images captured'
      })
      .options('build', {
        default: true,
        boolean: true,
        describe: 'Skip slide capture and build a PDF from images in the target directory'
      })
      .options('capture', {
        default: true,
        boolean: true,
        describe: 'Capture the slides, but do not render to PDF'
      })
      .options('controls', {
        default: false,
        describe: 'Show the Reveal.js controls'
      })
      .options('max-index', {
        default: 9999,
        describe: 'Maximum to use for frame indices'
      })
      .check(function(argv){
        if (['jpeg', 'png'].indexOf(argv.format)) {
          throw new TypeError('--format should be either png or jpeg.');
        }
      })
      .check(function(argv){
        if (argv.resolution && !argv.resolution.match(resolutionPattern)){
          throw new TypeError('Resolution must have format {height}x{width}');
        }
      })
      .usage('Save Reveal.js as PDF.\nUsage: $0 http://myrevealpres.com')
      .demand(1, 'Reveal.js URL needs to be provided as an argument.')
      .strict()
      .argv;

var targetDir = path.join(process.cwd(), argv.target);
var resolution = null;
var url = argv._[0];

fs.mkdir(targetDir, function (err) {
   if (err && err.code !== 'EEXIST') {
       console.log('Directory ' + targetDir + ' could not be created');
       process.exit(1);
   }
});

if (argv.resolution) {
  var matches = argv.resolution.match(pattern);

  resolution = { 'width': matches[1], 'height': matches[2] };
}

require('async').waterfall([
  function captureStep(done){
    if (!argv.capture){
      return done();
    }

    capture(url, targetDir, resolution, argv.quality, argv.format, argv.controls, argv.waitTime, argv.maxIndex, done);
  },
  function buildStep(done){
    build(targetDir, argv.output, argv.format, done);
  }
]);<commit_msg>Fix the `--capture` and `--build` flags behaviours.<commit_after>#!/usr/bin/env node
/*
 * Tool to crawl a Reveal.js slide deck and capture each fragment to build a PDF.
 * Author: Katie Miller (codemiller)
 */

var fs = require('fs');
var path = require('path');
var build = require('./../build.js');
var capture = require('./../capture.js');
var resolutionPattern = /(\d+)x(\d+)/;

var argv = require('yargs')
      .options('t', {
        alias: 'target',
        default: 'output',
        describe: 'Directory to write the images and PDF'
      })
      .options('o', {
        alias: 'output',
        default: 'presentation',
        describe: 'Filename for the PDF (without an extension)'
      })
      .options('resolution', {
        describe: 'Resolution for viewport; if unspecified, tool will use the Reveal.js slide dimensions'
      })
      .options('format', {
        default: 'jpeg',
        describe: 'Format of images captured'
      })
      .options('controls', {
        default: false,
        boolean: true
      })
      .options('wait', {
        alias: 'wait-time',
        default: 1000,
        describe: 'Number of milliseconds to wait for each slide/fragment to load before capture'
      })
      .options('quality', {
        default: 100,
        describe: 'Quality of images captured'
      })
      .options('build', {
        default: false,
        boolean: true,
        describe: 'Skip slide capture and build a PDF from images in the target directory'
      })
      .options('capture', {
        default: false,
        boolean: true,
        describe: 'Capture the slides, but do not render to PDF'
      })
      .options('controls', {
        default: false,
        describe: 'Show the Reveal.js controls'
      })
      .options('max-index', {
        default: 9999,
        describe: 'Maximum to use for frame indices'
      })
      .check(function(argv){
        if (['jpeg', 'png'].indexOf(argv.format)) {
          throw new TypeError('--format should be either png or jpeg.');
        }
      })
      .check(function(argv){
        if (argv.resolution && !argv.resolution.match(resolutionPattern)){
          throw new TypeError('Resolution must have format {height}x{width}');
        }
      })
      .usage('Save Reveal.js as PDF.\nUsage: $0 http://myrevealpres.com')
      .example('$0 http://myrevealpres.com --capture', 'downloads the slides only')
      .example('$0 http://myrevealpres.com --build', 'renders the PDF only')
      .demand(1, 'Reveal.js URL needs to be provided as an argument.')
      .strict()
      .argv;

var targetDir = path.join(process.cwd(), argv.target);
var resolution = null;
var url = argv._[0];
var buildAndCatpure = argv.capture === false && argv.build === false;

fs.mkdir(targetDir, function (err) {
   if (err && err.code !== 'EEXIST') {
       console.log('Directory ' + targetDir + ' could not be created');
       process.exit(1);
   }
});

if (argv.resolution) {
  var matches = argv.resolution.match(pattern);

  resolution = { 'width': matches[1], 'height': matches[2] };
}

require('async').waterfall([
  function captureStep(done){
    if (!argv.capture && !buildAndCatpure){
      return done();
    }

    capture(url, targetDir, resolution, argv.quality, argv.format, argv.controls, argv.waitTime, argv.maxIndex, done);
  },
  function buildStep(done){
    if (!argv.build && !buildAndCatpure){
      return done();
    }

    build(targetDir, argv.output, argv.format, done);
  }
]);<|endoftext|>