<commit_before>"use strict";

describe("fs", function () {

    var data = "test/data";

    describe("gpf.fs.getFileStorage", function () {

        function _createPath (path) {
            var parts = path.split("/"),
                index = -1,
                iFileStorage = gpf.fs.getFileStorage();
            function processPart () {
                ++index;
                if (index === parts.length) {
                    return Promise.resolve();
                }
                var subPath = parts.slice(0, index + 1).join("/");
                return iFileStorage.getInfo(subPath)
                    .then(function (info) {
                        if (info.type === gpf.fs.types.directory) {
                            return processPart();
                        }
                        if (info.type === gpf.fs.types.notFound) {
                            return iFileStorage.createDirectory(subPath).then(processPart);
                        }
                        return Promise.reject("ERROR");
                    });
            }
            return processPart();
        }

        function _removePath (path) {
            var parts = path.split("/"),
                iFileStorage = gpf.fs.getFileStorage();
            function processPart () {
                var subPath = parts.join("/");
                return iFileStorage.deleteDirectory(subPath)
                    .then(function () {
                        parts.pop();
                        if (parts.length > 3) {
                            return processPart();
                        }
                    });
            }
            return processPart();
        }

        function _checkFileBin (info) {
            assert(info.type === gpf.fs.types.file);
            assert(info.fileName === "file.bin");
            assert(info.filePath.indexOf(data + "/file.bin") !== -1);
            assert(info.size === 256);
            assert(info.createdDateTime instanceof Date);
            assert(info.createdDateTime.getUTCFullYear() > 2010);
            assert(info.modifiedDateTime instanceof Date);
            assert(info.modifiedDateTime.getUTCFullYear() > 2010);
        }

        function _checkFolder (info) {
            assert(info.type === gpf.fs.types.directory);
            assert(info.fileName === "folder");
            assert(info.filePath.indexOf(data + "/folder") !== -1);
            assert(info.createdDateTime instanceof Date);
            assert(info.createdDateTime.getUTCFullYear() > 2010);
            assert(info.modifiedDateTime instanceof Date);
            assert(info.modifiedDateTime.getUTCFullYear() > 2010);
        }

        if (gpf.fs.getFileStorage()) {

            var tmp = "tmp/fs/" + gpf.host() + "/"
                    + (new Date()).toISOString().replace(/\-|T|\:|\.|Z/g, "")
                    + "/" + Math.floor(100 * Math.random());

            it("returns a file storage interface", function () {
                assert(gpf.interfaces.isImplementedBy(gpf.interfaces.IFileStorage, gpf.fs.getFileStorage()));
            });

            describe("getInfo", function () {

                it("can get file information", function (done) {
                    gpf.fs.getFileStorage().getInfo(gpf.path.join(data, "file.bin"))
                        .then(_checkFileBin)
                        .then(done, done);
                });

                it("can get directory information", function (done) {
                    gpf.fs.getFileStorage().getInfo(gpf.path.join(data, "folder"))
                        .then(_checkFolder)
                        .then(done, done);
                });

                it("does not fail if the file does not exist", function (done) {
                    gpf.fs.getFileStorage().getInfo(gpf.path.join(data, "nope"))
                        .then(function (info) {
                            assert(info.type === gpf.fs.types.notFound);
                        })
                        .then(done, done);
                });

            });

            describe("openTextStream", function () {

                function _read (path, content) {
                    var iFileStorage = gpf.fs.getFileStorage(),
                        iWritableStream = new gpf.stream.WritableString();
                    return iFileStorage.openTextStream(path, gpf.fs.openFor.reading)
                        .then(function (iReadableStream) {
                            assert(gpf.interfaces.isImplementedBy(gpf.interfaces.IReadableStream, iReadableStream));
                            return iReadableStream.read(iWritableStream)
                                .then(function () {
                                    if (content) {
                                        assert(iWritableStream.toString() === content);
                                    }
                                    return iFileStorage.close(iReadableStream)
                                        .then(function () {
                                            return iWritableStream.toString();
                                        });
                                });
                        });
                }

                describe("read", function () {

                    it("reads a small text file", function (done) {
                        _read(gpf.path.join(data, "folder/hello world.txt"), "hello world\n")
                            .then(function () {
                                done();
                            }, done);
                    });

                    it("reads a larger text file", function (done) {
                        _read(gpf.path.join(data, "folder/lorem ipsum.txt"))
                            .then(function (content) {
                                assert(content.indexOf("Phasellus posuere.") !== -1); // Should contain it
                                done();
                            })["catch"](done);
                    });

                });

                describe("write", function () {

                    function _write (path, content) {
                        var iFileStorage = gpf.fs.getFileStorage();
                        return iFileStorage.openTextStream(path, gpf.fs.openFor.appending)
                            .then(function (iWritableStream) {
                                return iWritableStream.write(content)
                                    .then(function () {
                                        return iFileStorage.close(iWritableStream);
                                    });
                            });
                    }

                    before(function (done) {
                        _createPath(tmp)
                            .then(function () {
                                done();
                            }, done);
                    });

                    after(function (done) {
                        _removePath(tmp)
                            .then(function () {
                                done();
                            }, done);
                    });

                    describe("initial", function () {

                        var filePath = gpf.path.join(tmp, "hello world.txt");

                        before(function (done) {
                            _write(filePath, "hello world\n")
                                .then(done, done);
                        });

                        after(function (done) {
                            gpf.fs.getFileStorage().deleteFile(filePath)
                                .then(done, done);
                        });

                        it("writes a text file", function (done) {
                            _read(filePath, "hello world\n")
                                .then(function () {
                                    done();
                                }, done);
                        });

                        describe("append", function () {

                            it("appends a text file", function (done) {
                                _write(filePath, "goodbye\n")
                                    .then(function () {
                                        return _read(filePath, "hello world\ngoodbye\n");
                                    })
                                    .then(function () {
                                        done();
                                    }, done);
                            });

                        });

                    });

                });

            });

            describe("close", function () {

                it("rejects invalid streams", function (done) {
                    gpf.fs.getFileStorage().close({})
                        .then(function () {
                            throw new Error();
                        })
                        .then(undefined, function (e) {
                            assert(e instanceof gpf.Error.IncompatibleStream);
                        })
                        .then(done, done);
                });

            });

            describe("explore", function () {

                it("lists the content of a folder", function (done) {
                    gpf.fs.getFileStorage().explore(data)
                        .then(function (iEnumerator) {
                            assert(gpf.interfaces.isImplementedBy(gpf.interfaces.IEnumerator, iEnumerator));

                            function onInfo (info) {
                                var currentInfo = iEnumerator.getCurrent();
                                if (info.type === gpf.fs.types.file) {
                                    _checkFileBin(info);
                                    _checkFileBin(currentInfo);
                                } else {
                                    _checkFolder(info);
                                    _checkFolder(currentInfo);
                                }
                                return iEnumerator.moveNext();
                            }

                            return iEnumerator.reset()
                                .then(function () {
                                    return iEnumerator.moveNext();
                                })
                                .then(onInfo)
                                .then(onInfo)
                                .then(function (info) {
                                    assert(info === undefined);
                                })
                                .then(done, done);
                        });
                });

                it("fails on a file", function (done) {
                    gpf.fs.getFileStorage().explore(gpf.path.join(data, "file.bin"))
                        .then(function () {
                            done(new Error("unexpected"));
                        }, function () {
                            done();
                        });
                });

                it("fails with an invalid path", function (done) {
                    gpf.fs.getFileStorage().explore(gpf.path.join(data, "nothing"))
                        .then(function () {
                            done(new Error("unexpected"));
                        }, function () {
                            done();
                        });
                });

            });

        } else {

            it("does not return a file storage interface", function () {
                var iFileStorage = gpf.fs.getFileStorage();
                assert(null === iFileStorage);
            });

        }

    });

    if (gpf.host() !== gpf.hosts.browser) {

        describe("gpf.fs.read", function () {

            it("gives a generic read access to the file system", function (done) {
                gpf.fs.read(gpf.path.join(data, "folder/hello world.txt"))
                    .then(function (content) {
                        try {
                            assert(content === "hello world\n");
                            done();
                        } catch (e) {
                            done(e);
                        }
                    }, done);
            });

            it("fails if the file does not exist", function (done) {
                gpf.fs.read(gpf.path.join(data, "nope"))
                    .then(function () {
                        done(new Error("Should not happen"));

                    }, function (reason) {
                        try {
                            assert(reason instanceof Error);
                            done();
                        } catch (e) {
                            done(e);
                        }
                    });
            });

        });

    }

});
<commit_msg>Removes useless escaping (#231)<commit_after>"use strict";

describe("fs", function () {

    var data = "test/data";

    describe("gpf.fs.getFileStorage", function () {

        function _createPath (path) {
            var parts = path.split("/"),
                index = -1,
                iFileStorage = gpf.fs.getFileStorage();
            function processPart () {
                ++index;
                if (index === parts.length) {
                    return Promise.resolve();
                }
                var subPath = parts.slice(0, index + 1).join("/");
                return iFileStorage.getInfo(subPath)
                    .then(function (info) {
                        if (info.type === gpf.fs.types.directory) {
                            return processPart();
                        }
                        if (info.type === gpf.fs.types.notFound) {
                            return iFileStorage.createDirectory(subPath).then(processPart);
                        }
                        return Promise.reject("ERROR");
                    });
            }
            return processPart();
        }

        function _removePath (path) {
            var parts = path.split("/"),
                iFileStorage = gpf.fs.getFileStorage();
            function processPart () {
                var subPath = parts.join("/");
                return iFileStorage.deleteDirectory(subPath)
                    .then(function () {
                        parts.pop();
                        if (parts.length > 3) {
                            return processPart();
                        }
                    });
            }
            return processPart();
        }

        function _checkFileBin (info) {
            assert(info.type === gpf.fs.types.file);
            assert(info.fileName === "file.bin");
            assert(info.filePath.indexOf(data + "/file.bin") !== -1);
            assert(info.size === 256);
            assert(info.createdDateTime instanceof Date);
            assert(info.createdDateTime.getUTCFullYear() > 2010);
            assert(info.modifiedDateTime instanceof Date);
            assert(info.modifiedDateTime.getUTCFullYear() > 2010);
        }

        function _checkFolder (info) {
            assert(info.type === gpf.fs.types.directory);
            assert(info.fileName === "folder");
            assert(info.filePath.indexOf(data + "/folder") !== -1);
            assert(info.createdDateTime instanceof Date);
            assert(info.createdDateTime.getUTCFullYear() > 2010);
            assert(info.modifiedDateTime instanceof Date);
            assert(info.modifiedDateTime.getUTCFullYear() > 2010);
        }

        if (gpf.fs.getFileStorage()) {

            var tmp = "tmp/fs/" + gpf.host() + "/"
                    + (new Date()).toISOString().replace(/-|T|:|\.|Z/g, "")
                    + "/" + Math.floor(100 * Math.random());

            it("returns a file storage interface", function () {
                assert(gpf.interfaces.isImplementedBy(gpf.interfaces.IFileStorage, gpf.fs.getFileStorage()));
            });

            describe("getInfo", function () {

                it("can get file information", function (done) {
                    gpf.fs.getFileStorage().getInfo(gpf.path.join(data, "file.bin"))
                        .then(_checkFileBin)
                        .then(done, done);
                });

                it("can get directory information", function (done) {
                    gpf.fs.getFileStorage().getInfo(gpf.path.join(data, "folder"))
                        .then(_checkFolder)
                        .then(done, done);
                });

                it("does not fail if the file does not exist", function (done) {
                    gpf.fs.getFileStorage().getInfo(gpf.path.join(data, "nope"))
                        .then(function (info) {
                            assert(info.type === gpf.fs.types.notFound);
                        })
                        .then(done, done);
                });

            });

            describe("openTextStream", function () {

                function _read (path, content) {
                    var iFileStorage = gpf.fs.getFileStorage(),
                        iWritableStream = new gpf.stream.WritableString();
                    return iFileStorage.openTextStream(path, gpf.fs.openFor.reading)
                        .then(function (iReadableStream) {
                            assert(gpf.interfaces.isImplementedBy(gpf.interfaces.IReadableStream, iReadableStream));
                            return iReadableStream.read(iWritableStream)
                                .then(function () {
                                    if (content) {
                                        assert(iWritableStream.toString() === content);
                                    }
                                    return iFileStorage.close(iReadableStream)
                                        .then(function () {
                                            return iWritableStream.toString();
                                        });
                                });
                        });
                }

                describe("read", function () {

                    it("reads a small text file", function (done) {
                        _read(gpf.path.join(data, "folder/hello world.txt"), "hello world\n")
                            .then(function () {
                                done();
                            }, done);
                    });

                    it("reads a larger text file", function (done) {
                        _read(gpf.path.join(data, "folder/lorem ipsum.txt"))
                            .then(function (content) {
                                assert(content.indexOf("Phasellus posuere.") !== -1); // Should contain it
                                done();
                            })["catch"](done);
                    });

                });

                describe("write", function () {

                    function _write (path, content) {
                        var iFileStorage = gpf.fs.getFileStorage();
                        return iFileStorage.openTextStream(path, gpf.fs.openFor.appending)
                            .then(function (iWritableStream) {
                                return iWritableStream.write(content)
                                    .then(function () {
                                        return iFileStorage.close(iWritableStream);
                                    });
                            });
                    }

                    before(function (done) {
                        _createPath(tmp)
                            .then(function () {
                                done();
                            }, done);
                    });

                    after(function (done) {
                        _removePath(tmp)
                            .then(function () {
                                done();
                            }, done);
                    });

                    describe("initial", function () {

                        var filePath = gpf.path.join(tmp, "hello world.txt");

                        before(function (done) {
                            _write(filePath, "hello world\n")
                                .then(done, done);
                        });

                        after(function (done) {
                            gpf.fs.getFileStorage().deleteFile(filePath)
                                .then(done, done);
                        });

                        it("writes a text file", function (done) {
                            _read(filePath, "hello world\n")
                                .then(function () {
                                    done();
                                }, done);
                        });

                        describe("append", function () {

                            it("appends a text file", function (done) {
                                _write(filePath, "goodbye\n")
                                    .then(function () {
                                        return _read(filePath, "hello world\ngoodbye\n");
                                    })
                                    .then(function () {
                                        done();
                                    }, done);
                            });

                        });

                    });

                });

            });

            describe("close", function () {

                it("rejects invalid streams", function (done) {
                    gpf.fs.getFileStorage().close({})
                        .then(function () {
                            throw new Error();
                        })
                        .then(undefined, function (e) {
                            assert(e instanceof gpf.Error.IncompatibleStream);
                        })
                        .then(done, done);
                });

            });

            describe("explore", function () {

                it("lists the content of a folder", function (done) {
                    gpf.fs.getFileStorage().explore(data)
                        .then(function (iEnumerator) {
                            assert(gpf.interfaces.isImplementedBy(gpf.interfaces.IEnumerator, iEnumerator));

                            function onInfo (info) {
                                var currentInfo = iEnumerator.getCurrent();
                                if (info.type === gpf.fs.types.file) {
                                    _checkFileBin(info);
                                    _checkFileBin(currentInfo);
                                } else {
                                    _checkFolder(info);
                                    _checkFolder(currentInfo);
                                }
                                return iEnumerator.moveNext();
                            }

                            return iEnumerator.reset()
                                .then(function () {
                                    return iEnumerator.moveNext();
                                })
                                .then(onInfo)
                                .then(onInfo)
                                .then(function (info) {
                                    assert(info === undefined);
                                })
                                .then(done, done);
                        });
                });

                it("fails on a file", function (done) {
                    gpf.fs.getFileStorage().explore(gpf.path.join(data, "file.bin"))
                        .then(function () {
                            done(new Error("unexpected"));
                        }, function () {
                            done();
                        });
                });

                it("fails with an invalid path", function (done) {
                    gpf.fs.getFileStorage().explore(gpf.path.join(data, "nothing"))
                        .then(function () {
                            done(new Error("unexpected"));
                        }, function () {
                            done();
                        });
                });

            });

        } else {

            it("does not return a file storage interface", function () {
                var iFileStorage = gpf.fs.getFileStorage();
                assert(null === iFileStorage);
            });

        }

    });

    if (gpf.host() !== gpf.hosts.browser) {

        describe("gpf.fs.read", function () {

            it("gives a generic read access to the file system", function (done) {
                gpf.fs.read(gpf.path.join(data, "folder/hello world.txt"))
                    .then(function (content) {
                        try {
                            assert(content === "hello world\n");
                            done();
                        } catch (e) {
                            done(e);
                        }
                    }, done);
            });

            it("fails if the file does not exist", function (done) {
                gpf.fs.read(gpf.path.join(data, "nope"))
                    .then(function () {
                        done(new Error("Should not happen"));

                    }, function (reason) {
                        try {
                            assert(reason instanceof Error);
                            done();
                        } catch (e) {
                            done(e);
                        }
                    });
            });

        });

    }

});
<|endoftext|>


<commit_before>import router from './swRouter';

let total = 240;
let testDB = new Array(total)
	.fill()
	.map((_, index) => ({
		id: `id_${index}`,
		name: `User ${index}`,
		fee: 200000,
		desc: `I am User no. ${index}`,
		tags: ['hello', 'world'],
		avatar: `https://unsplash.it/100/100/?random=${index}`,
		pet: {
			name: 'Bobby',
			type: 'dog',
			languages: [
				{ name: 'Javascript', score: 70 },
				{ name: 'HTML', score: 90 },
				{ name: 'CSS', score: 32 },
			],
		},
		check: Math.random() > 0.5,
		num: Math.floor(101 * Math.random(0)),
		createdAt: new Date(
			Date.now() - (total - index) * 1000 * 60 * 60 * 24
		).toISOString(),
	}))
	.reverse();

const validAccessToken = 'FAKE_ACCESS_TOKEN_FOR_DEMO_ONLY';
const expiresIn = 7200;

const verify = async (ctx, next) => {
	const { accessToken } = ctx.request.query || {};
	if (!accessToken) {
		ctx.status = 401;
	} else if (accessToken !== validAccessToken) {
		ctx.status = 403;
	} else {
		await next();
	}
};

router
	.get('/api/auth/getUser', verify, async (ctx) => {
		ctx.body = { accessToken: validAccessToken, expiresIn };
	})
	.post('/api/auth/login', async (ctx) => {
		const { username, password } = ctx.request.body;
		if (username === 'admin' && password === '000000') {
			ctx.body = { accessToken: validAccessToken, expiresIn };
		} else {
			ctx.status = 400;
			ctx.body = {
				reason: 'Invalid username or password',
			};
		}
	})
	.get('/api/test/:key', verify, async (ctx) => {
		const { key } = ctx.params;
		const data = testDB.find(({ id }) => id === key);
		if (data) ctx.body = data;
		else ctx.status = 404;
	})
	.put('/api/test/:key', verify, async (ctx) => {
		const { body } = ctx.request;
		const { key } = ctx.params;
		const data = testDB.find(({ id }) => id === key);
		if (data) ctx.body = Object.assign(data, body);
		else ctx.status = 404;
	})
	.get('/api/test', verify, async (ctx) => {
		const { page, count, id, check } = ctx.request.query;
		const start = (page - 1) * +count;
		const db =
			check === undefined ?
				testDB :
				testDB.filter((item) => item.check === (check && check !== 'false'));
		ctx.body = {
			list: id ? [db[0]] : db.slice(start, start + +count),
			total: id ? 1 : total,
		};
	})
	.post('/api/test', verify, async (ctx) => {
		const { body } = ctx.request;
		body.id = `id_${total}`;
		total++;
		testDB.unshift(body);
		ctx.body = body;
	})
	.delete('/api/test/:keys', verify, async (ctx) => {
		const { keys } = ctx.params;
		const keysArr = keys.split(',');
		testDB = testDB.filter((data) => !keysArr.includes(data.id));
		ctx.body = { ok: true };
	})
	.get('/api/article/:key', verify, async (ctx) => {
		const { key } = ctx.params;
		const data = testDB.find(({ id }) => id === key);
		if (data) ctx.body = data;
		else ctx.status = 404;
	})
	.get('/api/article', verify, async (ctx) => {
		const { cursor, count } = ctx.request.query;
		const size = +count;
		const index = cursor ? testDB.findIndex((item) => item.id === cursor) : 0;
		if (index < 0) {
			ctx.body = { list: [], nextCursor: null };
		} else {
			const nextCursorIndex = index + size;
			const nextCursor =
				nextCursorIndex >= testDB.length ? null : testDB[nextCursorIndex].id;
			ctx.body = {
				list: testDB.slice(index, index + size),
				nextCursor,
			};
		}
	})
	.put('/api/article/:keys', verify, async (ctx) => {
		const { request: { body }, params: { keys } } = ctx;
		const keysArr = keys.split(',');
		for (const data of testDB) {
			if (keysArr.includes(data.id)) {
				Object.assign(data, body, { id: data.id });
			}
		}
		ctx.body = { ok: true };
	})
	.delete('/api/article/:keys', verify, async (ctx) => {
		const { keys } = ctx.params;
		const keysArr = keys.split(',');
		testDB = testDB.filter((data) => !keysArr.includes(data.id));
		ctx.body = { ok: true };
	})
	.get('/api/bar', verify, async (ctx) => {
		const { page, count, id } = ctx.request.query;
		const start = (page - 1) * +count;
		ctx.body = {
			list: id ? [testDB[0]] : testDB.slice(start, start + +count),
			total: id ? 1 : total,
		};
	})
	.post('/api/bar', verify, async (ctx) => {
		const { body } = ctx.request;
		body.id = `id_${total}`;
		total++;
		testDB.unshift(body);
		ctx.body = body;
	})
	.post('/api/test/:keys/bar', verify, async (ctx) => {
		const { request: { body }, params: { keys } } = ctx;
		ctx.body = { ...body, keys };
	})
	.post('/api2/test/:keys/bar/:barKeys', verify, async (ctx) => {
		const { params: { keys, barKeys } } = ctx;
		ctx.body = { keys, barKeys };
	})
	.post('/api/upload/file', async (ctx) => {
		ctx.body = { url: `https://unsplash.it/100/100/?random=${Math.random()}` };
	})
	.post('/api/upload/image', async (ctx) => {
		ctx.body = { url: `https://unsplash.it/100/100/?random=${Math.random()}` };
	});
<commit_msg>fix test server bug<commit_after>import router from './swRouter';

let total = 240;
let testDB = new Array(total)
	.fill()
	.map((_, index) => ({
		id: `id_${index}`,
		name: `User ${index}`,
		fee: 200000,
		desc: `I am User no. ${index}`,
		tags: ['hello', 'world'],
		avatar: `https://unsplash.it/100/100/?random=${index}`,
		pet: {
			name: 'Bobby',
			type: 'dog',
			languages: [
				{ name: 'Javascript', score: 70 },
				{ name: 'HTML', score: 90 },
				{ name: 'CSS', score: 32 },
			],
		},
		check: Math.random() > 0.5,
		num: Math.floor(101 * Math.random(0)),
		createdAt: new Date(
			Date.now() - (total - index) * 1000 * 60 * 60 * 24
		).toISOString(),
	}))
	.reverse();

const validAccessToken = 'FAKE_ACCESS_TOKEN_FOR_DEMO_ONLY';
const expiresIn = 7200;

const verify = async (ctx, next) => {
	const { accessToken } = ctx.request.query || {};
	if (!accessToken) {
		ctx.status = 401;
	} else if (accessToken !== validAccessToken) {
		ctx.status = 403;
	} else {
		await next();
	}
};

router
	.get('/api/auth/getUser', verify, async (ctx) => {
		ctx.body = { accessToken: validAccessToken, expiresIn };
	})
	.post('/api/auth/login', async (ctx) => {
		const { username, password } = ctx.request.body;
		if (username === 'admin' && password === '000000') {
			ctx.body = { accessToken: validAccessToken, expiresIn };
		} else {
			ctx.status = 400;
			ctx.body = {
				reason: 'Invalid username or password',
			};
		}
	})
	.get('/api/test/:key', verify, async (ctx) => {
		const { key } = ctx.params;
		const data = testDB.find(({ id }) => id === key);
		if (data) ctx.body = data;
		else ctx.status = 404;
	})
	.put('/api/test/:key', verify, async (ctx) => {
		const { body } = ctx.request;
		const { key } = ctx.params;
		const data = testDB.find(({ id }) => id === key);
		if (data) ctx.body = Object.assign(data, body);
		else ctx.status = 404;
	})
	.get('/api/test', verify, async (ctx) => {
		const { page, count, id, check } = ctx.request.query;
		const start = (page - 1) * +count;
		const db =
			check === undefined ?
				testDB :
				testDB.filter((item) => item.check === (check && check !== 'false'));
		ctx.body = {
			list: id ? [db[0]] : db.slice(start, start + +count),
			total: id ? 1 : total,
		};
	})
	.post('/api/test', verify, async (ctx) => {
		const { body } = ctx.request;
		body.id = `id_${total}`;
		total++;
		testDB.unshift(body);
		ctx.body = body;
	})
	.delete('/api/test/:keys', verify, async (ctx) => {
		const { keys } = ctx.params;
		const keysArr = keys.split(',');
		testDB = testDB.filter((data) => !keysArr.includes(data.id));
		ctx.body = { ok: true };
	})
	.get('/api/article/:key', verify, async (ctx) => {
		const { key } = ctx.params;
		const data = testDB.find(({ id }) => id === key);
		if (data) ctx.body = data;
		else ctx.status = 404;
	})
	.get('/api/article', verify, async (ctx) => {
		const { cursor, count } = ctx.request.query;
		const size = +count;
		const index = cursor ? testDB.findIndex((item) => item.id === cursor) : 0;
		if (index < 0) {
			ctx.body = { list: [], nextCursor: null };
		} else {
			const nextCursorIndex = index + size;
			const nextCursor =
				nextCursorIndex >= testDB.length ? null : testDB[nextCursorIndex].id;
			ctx.body = {
				list: testDB.slice(index, index + size),
				nextCursor,
			};
		}
	})
	.post('/api/article', verify, async (ctx) => {
		const { body } = ctx.request;
		body.id = `id_${total}`;
		total++;
		testDB.unshift(body);
		ctx.body = body;
	})
	.put('/api/article/:keys', verify, async (ctx) => {
		const {
			request: { body },
			params: { keys },
		} = ctx;
		const keysArr = keys.split(',');
		for (const data of testDB) {
			if (keysArr.includes(data.id)) {
				Object.assign(data, body, { id: data.id });
			}
		}
		ctx.body = { ok: true };
	})
	.delete('/api/article/:keys', verify, async (ctx) => {
		const { keys } = ctx.params;
		const keysArr = keys.split(',');
		testDB = testDB.filter((data) => !keysArr.includes(data.id));
		ctx.body = { ok: true };
	})
	.get('/api/bar', verify, async (ctx) => {
		const { page, count, id } = ctx.request.query;
		const start = (page - 1) * +count;
		ctx.body = {
			list: id ? [testDB[0]] : testDB.slice(start, start + +count),
			total: id ? 1 : total,
		};
	})
	.post('/api/bar', verify, async (ctx) => {
		const { body } = ctx.request;
		body.id = `id_${total}`;
		total++;
		testDB.unshift(body);
		ctx.body = body;
	})
	.post('/api/test/:keys/bar', verify, async (ctx) => {
		const {
			request: { body },
			params: { keys },
		} = ctx;
		ctx.body = { ...body, keys };
	})
	.post('/api2/test/:keys/bar/:barKeys', verify, async (ctx) => {
		const {
			params: { keys, barKeys },
		} = ctx;
		ctx.body = { keys, barKeys };
	})
	.post('/api/upload/file', async (ctx) => {
		ctx.body = { url: `https://unsplash.it/100/100/?random=${Math.random()}` };
	})
	.post('/api/upload/image', async (ctx) => {
		ctx.body = { url: `https://unsplash.it/100/100/?random=${Math.random()}` };
	});
<|endoftext|>


<commit_before>const request = require('request')
const Settings = require('settings-sharelatex')
const logger = require('logger-sharelatex')
const AuthenticationController = require('../Authentication/AuthenticationController')

const TEN_SECONDS = 1000 * 10

const languageCodeIsSupported = code =>
  Settings.languages.some(lang => lang.code === code)

module.exports = {
  proxyRequestToSpellingApi(req, res) {
    const { language } = req.body
    if (language && !languageCodeIsSupported(language)) {
      logger.warn(`language_code=${language} not supported`)
      return res.status(200).send(JSON.stringify({ misspellings: [] }))
    }

    const userId = AuthenticationController.getLoggedInUserId(req)
    let url = req.url.slice('/spelling'.length)
    url = `/user/${userId}${url}`
    req.headers['Host'] = Settings.apis.spelling.host
    return request({
      url: Settings.apis.spelling.url + url,
      method: req.method,
      headers: req.headers,
      json: req.body,
      timeout: TEN_SECONDS
    })
      .on('error', function(error) {
        logger.error({ err: error }, 'Spelling API error')
        return res.status(500).end()
      })
      .pipe(res)
  }
}
<commit_msg>set log level to info for unsupported languages<commit_after>const request = require('request')
const Settings = require('settings-sharelatex')
const logger = require('logger-sharelatex')
const AuthenticationController = require('../Authentication/AuthenticationController')

const TEN_SECONDS = 1000 * 10

const languageCodeIsSupported = code =>
  Settings.languages.some(lang => lang.code === code)

module.exports = {
  proxyRequestToSpellingApi(req, res) {
    const { language } = req.body
    if (language && !languageCodeIsSupported(language)) {
      logger.info({ language }, `language not supported`)
      return res.status(200).send(JSON.stringify({ misspellings: [] }))
    }

    const userId = AuthenticationController.getLoggedInUserId(req)
    let url = req.url.slice('/spelling'.length)
    url = `/user/${userId}${url}`
    req.headers['Host'] = Settings.apis.spelling.host
    return request({
      url: Settings.apis.spelling.url + url,
      method: req.method,
      headers: req.headers,
      json: req.body,
      timeout: TEN_SECONDS
    })
      .on('error', function(error) {
        logger.error({ err: error }, 'Spelling API error')
        return res.status(500).end()
      })
      .pipe(res)
  }
}
<|endoftext|>


<commit_before>function DataTableView(div) {
    this._div = div;
    this._pageSize = 20;
    this._showRecon = true;
    this._collapsedColumnNames = {};

    this._showRows(0);
}

DataTableView.prototype.resize = function() {
    var topHeight = this._div.find(".viewPanel-header").outerHeight(true);

    this._div.find(".data-table-container")
        .css("height", (this._div.innerHeight() - topHeight - 1) + "px")
        .css("display", "block");
};

DataTableView.prototype.update = function(onDone) {
    this._showRows(0, onDone);
};

DataTableView.prototype.render = function() {
    var self = this;

    var oldTableDiv = this._div.find(".data-table-container");
    var scrollLeft = (oldTableDiv.length > 0) ? oldTableDiv[0].scrollLeft : 0;

    var html = $(
        '
' +
            '
' +
                '	' +
                '	' +
                '	' +
            '
' +
        '
' +
        '

'
    );
    var elmts = DOM.bind(html);

    this._renderSummaryText(elmts.summary);
    this._renderPagingControls(elmts.pageSizeControls, elmts.pagingControls);
    this._renderDataTable(elmts.table[0]);

    this._div.empty().append(html);

    this.resize();

    elmts.dataTableContainer[0].scrollLeft = scrollLeft;
};

DataTableView.prototype._renderSummaryText = function(elmt) {
    var summaryText;

    var from = (theProject.rowModel.start + 1);
    var to = Math.min(theProject.rowModel.filtered, theProject.rowModel.start + theProject.rowModel.limit);
    if (theProject.rowModel.filtered == theProject.rowModel.total) {
        summaryText = from + ' – ' + to + ' of ' + (theProject.rowModel.total) + ' rows';
    } else {
        summaryText = from + ' – ' + to + ' of ' +
            (theProject.rowModel.filtered) + ' matching rows (' + (theProject.rowModel.total) + ' total)';
    }
    $('').html(summaryText).appendTo(elmt);
};

DataTableView.prototype._renderPagingControls = function(pageSizeControls, pagingControls) {
    var self = this;

    var firstPage = $('« first').appendTo(pagingControls);
    var previousPage = $('« previous').appendTo(pagingControls);
    if (theProject.rowModel.start > 0) {
        firstPage.addClass("action").click(function(evt) { self._onClickFirstPage(this, evt); });
        previousPage.addClass("action").click(function(evt) { self._onClickPreviousPage(this, evt); });
    } else {
        firstPage.addClass("inaction");
        previousPage.addClass("inaction");
    }
    $(' • ').appendTo(pagingControls);
    var nextPage = $('next page »').appendTo(pagingControls);
    var lastPage = $('last »').appendTo(pagingControls);
    if (theProject.rowModel.start + theProject.rowModel.limit < theProject.rowModel.filtered) {
        nextPage.addClass("action").click(function(evt) { self._onClickNextPage(this, evt); });
        lastPage.addClass("action").click(function(evt) { self._onClickLastPage(this, evt); });
    } else {
        nextPage.addClass("inaction");
        lastPage.addClass("inaction");
    }

    $('Show ').appendTo(pageSizeControls);
    var sizes = [ 10, 20, 25, 50 ];
    var renderPageSize = function(index) {
        var pageSize = sizes[index];
        var a = $('')
            .addClass("viewPanel-pagingControls-page")
            .appendTo(pageSizeControls);
        if (pageSize == self._pageSize) {
            a.text(pageSize).addClass("inaction");
        } else {
            a.text(pageSize).addClass("action").click(function(evt) {
                self._pageSize = pageSize;
                self.update();
            });
        }
    };
    for (var i = 0; i < sizes.length; i++) {
        renderPageSize(i);
    }
    $(' rows').appendTo(pageSizeControls);
};

DataTableView.prototype._renderDataTable = function(table) {
    var self = this;

    var columns = theProject.columnModel.columns;
    var columnGroups = theProject.columnModel.columnGroups;

    /*------------------------------------------------------------
     *  Column Group Headers
     *------------------------------------------------------------
     */

    var renderColumnKeys = function(keys) {
        if (keys.length > 0) {
            var tr = table.insertRow(table.rows.length);
            tr.insertCell(0); // star
            tr.insertCell(1); // flag
            tr.insertCell(2); // row index

            for (var c = 0; c < columns.length; c++) {
                var td = tr.insertCell(tr.cells.length);

                for (var k = 0; k < keys.length; k++) {
                    if (c == keys[k]) {
                        $('').attr("src", "images/down-arrow.png").appendTo(td);
                        break;
                    }
                }
            }
        }
    };

    var renderColumnGroups = function(groups, keys) {
        var nextLayer = [];

        if (groups.length > 0) {
            var tr = table.insertRow(table.rows.length);
            tr.insertCell(0); // star
            tr.insertCell(1); // flag
            tr.insertCell(2); // row index

            for (var c = 0; c < columns.length; c++) {
                var foundGroup = false;
                var columnGroup;

                for (var g = 0; g < groups.length; g++) {
                    columnGroup = groups[g];
                    if (columnGroup.startColumnIndex == c) {
                        foundGroup = true;
                        break;
                    }
                }

                var td = tr.insertCell(tr.cells.length);
                if (foundGroup) {
                    td.setAttribute("colspan", columnGroup.columnSpan);
                    td.style.background = "blue";

                    if (columnGroup.keyColumnIndex >= 0) {
                        keys.push(columnGroup.keyColumnIndex);
                    }

                    c += (columnGroup.columnSpan - 1);

                    if ("subgroups" in columnGroup) {
                        nextLayer = nextLayer.concat(columnGroup.subgroups);
                    }
                }
            }
        }

        renderColumnKeys(keys);

        if (nextLayer.length > 0) {
            renderColumnGroups(nextLayer, []);
        }
    };

    if (columnGroups.length > 0) {
        renderColumnGroups(
            columnGroups,
            [ theProject.columnModel.keyCellIndex ]
        );
    }

    /*------------------------------------------------------------
     *  Column Headers with Menus
     *------------------------------------------------------------
     */

    var trHead = table.insertRow(table.rows.length);
    DOM.bind(
        $(trHead.insertCell(trHead.cells.length))
            .attr("colspan", "3")
            .addClass("column-header")
            .html(
                '

' +
                    '' +
                        ' ' +
                    '	' +
                    ' 	' +
                '

'
            )
    ).dropdownMenu.click(function() {
        self._createMenuForAllColumns(this);
    });

    var createColumnHeader = function(column, index) {
        var td = trHead.insertCell(trHead.cells.length);
        $(td).addClass("column-header");

        if (column.name in self._collapsedColumnNames) {
            $(td).html(" ").attr("title", column.name).click(function(evt) {
                delete self._collapsedColumnNames[column.name];
                self.render();
            });
        } else {
            new DataTableColumnHeaderUI(self, column, index, td);
        }
    };

    for (var i = 0; i < columns.length; i++) {
        createColumnHeader(columns[i], i);
    }

    /*------------------------------------------------------------
     *  Data Cells
     *------------------------------------------------------------
     */

    var rows = theProject.rowModel.rows;
    var renderRow = function(tr, r, row, even) {
        $(tr).empty();

        var cells = row.cells;

        var tdStar = tr.insertCell(tr.cells.length);
        var star = $(' ')
            .addClass(row.starred ? "data-table-star-on" : "data-table-star-off")
            .appendTo(tdStar)
            .click(function() {
                var newStarred = !row.starred;

                Gridworks.postProcess(
                    "annotate-one-row",
                    { row: row.i, starred: newStarred },
                    null,
                    {},
                    {   onDone: function(o) {
                            row.starred = newStarred;
                            renderRow(tr, r, row, even);
                        }
                    },
                    "json"
                );
            });

        var tdFlag = tr.insertCell(tr.cells.length);
        var flag = $(' ')
            .addClass(row.flagged ? "data-table-flag-on" : "data-table-flag-off")
            .appendTo(tdFlag)
            .click(function() {
                var newFlagged = !row.flagged;

                Gridworks.postProcess(
                    "annotate-one-row",
                    { row: row.i, flagged: newFlagged },
                    null,
                    {},
                    {   onDone: function(o) {
                            row.flagged = newFlagged;
                            renderRow(tr, r, row, even);
                        }
                    },
                    "json"
                );
            });

        var tdIndex = tr.insertCell(tr.cells.length);
        if ("j" in row) {
            $(tr).addClass("record");
            $('

').html((row.j + 1) + ".").appendTo(tdIndex);
        } else {
            $('

').html(" ").appendTo(tdIndex);
        }

        if ("contextual" in row && row.contextual) {
            $(tr).addClass("contextual");
        }

        $(tr).addClass(even ? "even" : "odd");

        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var td = tr.insertCell(tr.cells.length);
            if (column.name in self._collapsedColumnNames) {
                td.innerHTML = " ";
            } else {
                var cell = (column.cellIndex < cells.length) ? cells[column.cellIndex] : null;
                new DataTableCellUI(self, cell, row.i, column.cellIndex, td);
            }
        }
    };

    var even = true;
    for (var r = 0; r < rows.length; r++) {
        var row = rows[r];
        var tr = table.insertRow(table.rows.length);
        if ("j" in row) {
            even = !even;
        }
        renderRow(tr, r, row, even);
    }
};

DataTableView.prototype._showRows = function(start, onDone) {
    var self = this;
    Gridworks.fetchRows(start, this._pageSize, function() {
        self.render();

        if (onDone) {
            onDone();
        }
    });
};

DataTableView.prototype._onClickPreviousPage = function(elmt, evt) {
    this._showRows(theProject.rowModel.start - this._pageSize);
};

DataTableView.prototype._onClickNextPage = function(elmt, evt) {
    this._showRows(theProject.rowModel.start + this._pageSize);
};

DataTableView.prototype._onClickFirstPage = function(elmt, evt) {
    this._showRows(0);
};

DataTableView.prototype._onClickLastPage = function(elmt, evt) {
    this._showRows(Math.floor(theProject.rowModel.filtered / this._pageSize) * this._pageSize);
};

DataTableView.prototype._createMenuForAllColumns = function(elmt) {
    self = this;
    MenuSystem.createAndShowStandardMenu([
        {   label: "Facet",
            submenu: [
                {
                    label: "Facet by Star",
                    click: function() {
                        ui.browsingEngine.addFacet(
                            "list",
                            {
                                "name" : "Starred Rows",
                                "columnName" : "",
                                "expression" : "row.starred"
                            },
                            {
                                "scroll" : false
                            }
                        );
                    }
                },
                {
                    label: "Facet by Flag",
                    click: function() {
                        ui.browsingEngine.addFacet(
                            "list",
                            {
                                "name" : "Flagged Rows",
                                "columnName" : "",
                                "expression" : "row.flagged"
                            },
                            {
                                "scroll" : false
                            }
                        );
                    }
                }
            ]
        },
        {   label: "Edit Rows",
            submenu: [
                {
                    label: "Star Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "starred" : "true" }, null, { rowMetadataChanged: true });
                    }
                },
                {
                    label: "Unstar Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "starred" : "false" }, null, { rowMetadataChanged: true });
                    }
                },
                {},
                {
                    label: "Flag Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "flagged" : "true" }, null, { rowMetadataChanged: true });
                    }
                },
                {
                    label: "Unflag Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "flagged" : "false" }, null, { rowMetadataChanged: true });
                    }
                },
                {},
                {
                    label: "Remove All Matching Rows",
                    click: function() {
                        Gridworks.postProcess("remove-rows", {}, null, { rowMetadataChanged: true });
                    }
                }
            ]
        },
        {   label: "View",
            submenu: [
                {
                    label: "Collapse All Columns",
                    click: function() {
                        for (var i = 0; i < theProject.columnModel.columns.length; i++) {
                            self._collapsedColumnNames[theProject.columnModel.columns[i].name] = true;
                        }
                        self.render();
                    }
                },
                {
                    label: "Expand All Columns",
                    click: function() {
                        self._collapsedColumnNames = [];
                        self.render();
                    }
                }
            ]
        }
    ], elmt, { width: "80px", horizontal: false });
};

DataTableView.prototype._updateCell = function(rowIndex, cellIndex, cell) {
    var rows = theProject.rowModel.rows;
    for (var r = 0; r < rows.length; r++) {
        var row = rows[r];
        if (row.i === rowIndex) {
            while (cellIndex >= row.cells.length) {
                row.cells.push(null);
            }
            row.cells[cellIndex] = cell;
            break;
        }
    }
};

DataTableView.sampleVisibleRows = function(column) {
    var rowIndices = [];
    var values = [];

    var rows = theProject.rowModel.rows;
    for (var r = 0; r < rows.length; r++) {
        var row = rows[r];

        rowIndices.push(row.i);

        var v = null;
        if (column && column.cellIndex < row.cells.length) {
            var cell = row.cells[column.cellIndex];
            if (cell !== null) {
                v = cell.v;
            }
        }
        values.push(v);
    }

    return {
        rowIndices: rowIndices,
        values: values
    };
};

DataTableView.promptExpressionOnVisibleRows = function(column, title, expression, onDone) {
    var o = DataTableView.sampleVisibleRows(column);

    var self = this;
    new ExpressionPreviewDialog(
        title,
        column.cellIndex,
        o.rowIndices,
        o.values,
        expression,
        onDone
    );
};

<commit_msg>style consistency<commit_after>function DataTableView(div) {
    this._div = div;
    this._pageSize = 20;
    this._showRecon = true;
    this._collapsedColumnNames = {};

    this._showRows(0);
}

DataTableView.prototype.resize = function() {
    var topHeight = this._div.find(".viewPanel-header").outerHeight(true);

    this._div.find(".data-table-container")
        .css("height", (this._div.innerHeight() - topHeight - 1) + "px")
        .css("display", "block");
};

DataTableView.prototype.update = function(onDone) {
    this._showRows(0, onDone);
};

DataTableView.prototype.render = function() {
    var self = this;

    var oldTableDiv = this._div.find(".data-table-container");
    var scrollLeft = (oldTableDiv.length > 0) ? oldTableDiv[0].scrollLeft : 0;

    var html = $(
        '
' +
            '
' +
                '	' +
                '	' +
                '	' +
            '
' +
        '
' +
        '

'
    );
    var elmts = DOM.bind(html);

    this._renderSummaryText(elmts.summary);
    this._renderPagingControls(elmts.pageSizeControls, elmts.pagingControls);
    this._renderDataTable(elmts.table[0]);

    this._div.empty().append(html);

    this.resize();

    elmts.dataTableContainer[0].scrollLeft = scrollLeft;
};

DataTableView.prototype._renderSummaryText = function(elmt) {
    var summaryText;

    var from = (theProject.rowModel.start + 1);
    var to = Math.min(theProject.rowModel.filtered, theProject.rowModel.start + theProject.rowModel.limit);
    if (theProject.rowModel.filtered == theProject.rowModel.total) {
        summaryText = from + ' – ' + to + ' of ' + (theProject.rowModel.total) + ' rows';
    } else {
        summaryText = from + ' – ' + to + ' of ' +
            (theProject.rowModel.filtered) + ' matching rows (' + (theProject.rowModel.total) + ' total)';
    }
    $('').html(summaryText).appendTo(elmt);
};

DataTableView.prototype._renderPagingControls = function(pageSizeControls, pagingControls) {
    var self = this;

    var firstPage = $('« first').appendTo(pagingControls);
    var previousPage = $('« previous').appendTo(pagingControls);
    if (theProject.rowModel.start > 0) {
        firstPage.addClass("action").click(function(evt) { self._onClickFirstPage(this, evt); });
        previousPage.addClass("action").click(function(evt) { self._onClickPreviousPage(this, evt); });
    } else {
        firstPage.addClass("inaction");
        previousPage.addClass("inaction");
    }
    $(' • ').appendTo(pagingControls);
    var nextPage = $('next page »').appendTo(pagingControls);
    var lastPage = $('last »').appendTo(pagingControls);
    if (theProject.rowModel.start + theProject.rowModel.limit < theProject.rowModel.filtered) {
        nextPage.addClass("action").click(function(evt) { self._onClickNextPage(this, evt); });
        lastPage.addClass("action").click(function(evt) { self._onClickLastPage(this, evt); });
    } else {
        nextPage.addClass("inaction");
        lastPage.addClass("inaction");
    }

    $('Show ').appendTo(pageSizeControls);
    var sizes = [ 10, 20, 25, 50 ];
    var renderPageSize = function(index) {
        var pageSize = sizes[index];
        var a = $('')
            .addClass("viewPanel-pagingControls-page")
            .appendTo(pageSizeControls);
        if (pageSize == self._pageSize) {
            a.text(pageSize).addClass("inaction");
        } else {
            a.text(pageSize).addClass("action").click(function(evt) {
                self._pageSize = pageSize;
                self.update();
            });
        }
    };
    for (var i = 0; i < sizes.length; i++) {
        renderPageSize(i);
    }
    $(' rows').appendTo(pageSizeControls);
};

DataTableView.prototype._renderDataTable = function(table) {
    var self = this;

    var columns = theProject.columnModel.columns;
    var columnGroups = theProject.columnModel.columnGroups;

    /*------------------------------------------------------------
     *  Column Group Headers
     *------------------------------------------------------------
     */

    var renderColumnKeys = function(keys) {
        if (keys.length > 0) {
            var tr = table.insertRow(table.rows.length);
            tr.insertCell(0); // star
            tr.insertCell(1); // flag
            tr.insertCell(2); // row index

            for (var c = 0; c < columns.length; c++) {
                var td = tr.insertCell(tr.cells.length);

                for (var k = 0; k < keys.length; k++) {
                    if (c == keys[k]) {
                        $('').attr("src", "images/down-arrow.png").appendTo(td);
                        break;
                    }
                }
            }
        }
    };

    var renderColumnGroups = function(groups, keys) {
        var nextLayer = [];

        if (groups.length > 0) {
            var tr = table.insertRow(table.rows.length);
            tr.insertCell(0); // star
            tr.insertCell(1); // flag
            tr.insertCell(2); // row index

            for (var c = 0; c < columns.length; c++) {
                var foundGroup = false;
                var columnGroup;

                for (var g = 0; g < groups.length; g++) {
                    columnGroup = groups[g];
                    if (columnGroup.startColumnIndex == c) {
                        foundGroup = true;
                        break;
                    }
                }

                var td = tr.insertCell(tr.cells.length);
                if (foundGroup) {
                    td.setAttribute("colspan", columnGroup.columnSpan);
                    td.style.background = "#FF6A00";

                    if (columnGroup.keyColumnIndex >= 0) {
                        keys.push(columnGroup.keyColumnIndex);
                    }

                    c += (columnGroup.columnSpan - 1);

                    if ("subgroups" in columnGroup) {
                        nextLayer = nextLayer.concat(columnGroup.subgroups);
                    }
                }
            }
        }

        renderColumnKeys(keys);

        if (nextLayer.length > 0) {
            renderColumnGroups(nextLayer, []);
        }
    };

    if (columnGroups.length > 0) {
        renderColumnGroups(
            columnGroups,
            [ theProject.columnModel.keyCellIndex ]
        );
    }

    /*------------------------------------------------------------
     *  Column Headers with Menus
     *------------------------------------------------------------
     */

    var trHead = table.insertRow(table.rows.length);
    DOM.bind(
        $(trHead.insertCell(trHead.cells.length))
            .attr("colspan", "3")
            .addClass("column-header")
            .html(
                '

' +
                    '' +
                        ' ' +
                    '	' +
                    ' 	' +
                '

'
            )
    ).dropdownMenu.click(function() {
        self._createMenuForAllColumns(this);
    });

    var createColumnHeader = function(column, index) {
        var td = trHead.insertCell(trHead.cells.length);
        $(td).addClass("column-header");

        if (column.name in self._collapsedColumnNames) {
            $(td).html(" ").attr("title", column.name).click(function(evt) {
                delete self._collapsedColumnNames[column.name];
                self.render();
            });
        } else {
            new DataTableColumnHeaderUI(self, column, index, td);
        }
    };

    for (var i = 0; i < columns.length; i++) {
        createColumnHeader(columns[i], i);
    }

    /*------------------------------------------------------------
     *  Data Cells
     *------------------------------------------------------------
     */

    var rows = theProject.rowModel.rows;
    var renderRow = function(tr, r, row, even) {
        $(tr).empty();

        var cells = row.cells;

        var tdStar = tr.insertCell(tr.cells.length);
        var star = $(' ')
            .addClass(row.starred ? "data-table-star-on" : "data-table-star-off")
            .appendTo(tdStar)
            .click(function() {
                var newStarred = !row.starred;

                Gridworks.postProcess(
                    "annotate-one-row",
                    { row: row.i, starred: newStarred },
                    null,
                    {},
                    {   onDone: function(o) {
                            row.starred = newStarred;
                            renderRow(tr, r, row, even);
                        }
                    },
                    "json"
                );
            });

        var tdFlag = tr.insertCell(tr.cells.length);
        var flag = $(' ')
            .addClass(row.flagged ? "data-table-flag-on" : "data-table-flag-off")
            .appendTo(tdFlag)
            .click(function() {
                var newFlagged = !row.flagged;

                Gridworks.postProcess(
                    "annotate-one-row",
                    { row: row.i, flagged: newFlagged },
                    null,
                    {},
                    {   onDone: function(o) {
                            row.flagged = newFlagged;
                            renderRow(tr, r, row, even);
                        }
                    },
                    "json"
                );
            });

        var tdIndex = tr.insertCell(tr.cells.length);
        if ("j" in row) {
            $(tr).addClass("record");
            $('

').html((row.j + 1) + ".").appendTo(tdIndex);
        } else {
            $('

').html(" ").appendTo(tdIndex);
        }

        if ("contextual" in row && row.contextual) {
            $(tr).addClass("contextual");
        }

        $(tr).addClass(even ? "even" : "odd");

        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var td = tr.insertCell(tr.cells.length);
            if (column.name in self._collapsedColumnNames) {
                td.innerHTML = " ";
            } else {
                var cell = (column.cellIndex < cells.length) ? cells[column.cellIndex] : null;
                new DataTableCellUI(self, cell, row.i, column.cellIndex, td);
            }
        }
    };

    var even = true;
    for (var r = 0; r < rows.length; r++) {
        var row = rows[r];
        var tr = table.insertRow(table.rows.length);
        if ("j" in row) {
            even = !even;
        }
        renderRow(tr, r, row, even);
    }
};

DataTableView.prototype._showRows = function(start, onDone) {
    var self = this;
    Gridworks.fetchRows(start, this._pageSize, function() {
        self.render();

        if (onDone) {
            onDone();
        }
    });
};

DataTableView.prototype._onClickPreviousPage = function(elmt, evt) {
    this._showRows(theProject.rowModel.start - this._pageSize);
};

DataTableView.prototype._onClickNextPage = function(elmt, evt) {
    this._showRows(theProject.rowModel.start + this._pageSize);
};

DataTableView.prototype._onClickFirstPage = function(elmt, evt) {
    this._showRows(0);
};

DataTableView.prototype._onClickLastPage = function(elmt, evt) {
    this._showRows(Math.floor(theProject.rowModel.filtered / this._pageSize) * this._pageSize);
};

DataTableView.prototype._createMenuForAllColumns = function(elmt) {
    self = this;
    MenuSystem.createAndShowStandardMenu([
        {   label: "Facet",
            submenu: [
                {
                    label: "Facet by Star",
                    click: function() {
                        ui.browsingEngine.addFacet(
                            "list",
                            {
                                "name" : "Starred Rows",
                                "columnName" : "",
                                "expression" : "row.starred"
                            },
                            {
                                "scroll" : false
                            }
                        );
                    }
                },
                {
                    label: "Facet by Flag",
                    click: function() {
                        ui.browsingEngine.addFacet(
                            "list",
                            {
                                "name" : "Flagged Rows",
                                "columnName" : "",
                                "expression" : "row.flagged"
                            },
                            {
                                "scroll" : false
                            }
                        );
                    }
                }
            ]
        },
        {   label: "Edit Rows",
            submenu: [
                {
                    label: "Star Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "starred" : "true" }, null, { rowMetadataChanged: true });
                    }
                },
                {
                    label: "Unstar Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "starred" : "false" }, null, { rowMetadataChanged: true });
                    }
                },
                {},
                {
                    label: "Flag Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "flagged" : "true" }, null, { rowMetadataChanged: true });
                    }
                },
                {
                    label: "Unflag Rows",
                    click: function() {
                        Gridworks.postProcess("annotate-rows", { "flagged" : "false" }, null, { rowMetadataChanged: true });
                    }
                },
                {},
                {
                    label: "Remove All Matching Rows",
                    click: function() {
                        Gridworks.postProcess("remove-rows", {}, null, { rowMetadataChanged: true });
                    }
                }
            ]
        },
        {   label: "View",
            submenu: [
                {
                    label: "Collapse All Columns",
                    click: function() {
                        for (var i = 0; i < theProject.columnModel.columns.length; i++) {
                            self._collapsedColumnNames[theProject.columnModel.columns[i].name] = true;
                        }
                        self.render();
                    }
                },
                {
                    label: "Expand All Columns",
                    click: function() {
                        self._collapsedColumnNames = [];
                        self.render();
                    }
                }
            ]
        }
    ], elmt, { width: "80px", horizontal: false });
};

DataTableView.prototype._updateCell = function(rowIndex, cellIndex, cell) {
    var rows = theProject.rowModel.rows;
    for (var r = 0; r < rows.length; r++) {
        var row = rows[r];
        if (row.i === rowIndex) {
            while (cellIndex >= row.cells.length) {
                row.cells.push(null);
            }
            row.cells[cellIndex] = cell;
            break;
        }
    }
};

DataTableView.sampleVisibleRows = function(column) {
    var rowIndices = [];
    var values = [];

    var rows = theProject.rowModel.rows;
    for (var r = 0; r < rows.length; r++) {
        var row = rows[r];

        rowIndices.push(row.i);

        var v = null;
        if (column && column.cellIndex < row.cells.length) {
            var cell = row.cells[column.cellIndex];
            if (cell !== null) {
                v = cell.v;
            }
        }
        values.push(v);
    }

    return {
        rowIndices: rowIndices,
        values: values
    };
};

DataTableView.promptExpressionOnVisibleRows = function(column, title, expression, onDone) {
    var o = DataTableView.sampleVisibleRows(column);

    var self = this;
    new ExpressionPreviewDialog(
        title,
        column.cellIndex,
        o.rowIndices,
        o.values,
        expression,
        onDone
    );
};

<|endoftext|>


<commit_before>L.TileLayer.WMTS = L.TileLayer.extend({

  defaultWmtsParams: {
    service: 'WMTS',
    request: 'GetTile',
    version: '1.0.0',
    layer: '',
    style: '',
    tilematrixSet: '',
    format: 'image/jpeg'
  },

  initialize: function(url, options) { // (String, Object)
    this._url = url;
    var wmtsParams = L.extend({}, this.defaultWmtsParams),
      tileSize = options.tileSize || this.options.tileSize;
    if (options.detectRetina && L.Browser.retina) {
      wmtsParams.width = wmtsParams.height = tileSize * 2;
    } else {
      wmtsParams.width = wmtsParams.height = tileSize;
    }
    for (var i in options) {
      // all keys that are not TileLayer options go to WMTS params
      if (!this.options.hasOwnProperty(i) && i != "matrixIds") {
        wmtsParams[i] = options[i];
      }
    }
    this.wmtsParams = wmtsParams;
    this.matrixIds = options.matrixIds || this.getDefaultMatrix();
    L.setOptions(this, options);
  },

  onAdd: function(map) {
    if (this.options.leafletObject) {
      this.options.leafletObject._map = this._map;
      if (this.getEvents) {
        var events = this.getEvents();
        map.on(events, this.options.leafletObject);
        this.once('remove', function() {
          map.off(events, this);
        }, this);
      }
      this.options.leafletObject._zoomAnimated = this._zoomAnimated;
      this.options.leafletObject._eventParents = this._eventParents;
      this.options.leafletObject._layers = this._layers;
      this.options.leafletObject._mapToAdd = this._mapToAdd;
      L.TileLayer.prototype.onAdd.call(this.options.leafletObject, map);
    } else {
      L.TileLayer.prototype.onAdd.call(this, map);
    }
  },

  getTileUrl: function(tilePoint, zoom) { // (Point, Number) -> String
    var map = this._map;
    crs = map.options.crs;
    tileSize = this.options.tileSize;
    nwPoint = tilePoint.multiplyBy(tileSize);
    //+/-1 in order to be on the tile
    nwPoint.x += 1;
    nwPoint.y -= 1;
    sePoint = nwPoint.add(new L.Point(tileSize, tileSize));
    nw = crs.project(map.unproject(nwPoint, zoom));
    se = crs.project(map.unproject(sePoint, zoom));
    tilewidth = se.x - nw.x;
    zoom = map.getZoom();
    ident = this.matrixIds[zoom].identifier;
    X0 = this.matrixIds[zoom].topLeftCorner.lng;
    Y0 = this.matrixIds[zoom].topLeftCorner.lat;
    tilecol = Math.floor((nw.x - X0) / tilewidth);
    tilerow = -Math.floor((nw.y - Y0) / tilewidth);
    // hack to fix bad tile location on north pole area
    tilerow = (Math.abs(tilerow) < 0.001)?0:tilerow;
    url = L.Util.template(this._url, {
      s: this._getSubdomain(tilePoint)
    });
    return url + L.Util.getParamString(this.wmtsParams, url) + "&tilematrix=" + ident + "&tilerow=" + tilerow + "&tilecol=" + tilecol;
  },

  setParams: function(params, noRedraw) {
    L.extend(this.wmtsParams, params);
    if (!noRedraw) {
      this.redraw();
    }
    return this;
  },

  getDefaultMatrix: function() {
    /**
     * the matrix3857 represents the projection
     * for in the IGN WMTS for the google coordinates.
     */
    var matrixIds3857 = new Array(22);
    for (var i = 0; i < 22; i++) {
      matrixIds3857[i] = {
        identifier: "" + i,
        topLeftCorner: new L.LatLng(20037508.3428, -20037508.3428)
      };
    }
    return matrixIds3857;
  }
});

L.tileLayer.wmts = function(url, options) {
  return new L.TileLayer.WMTS(url, options);
};
<commit_msg>-added a proxy to Leaflet wmts.<commit_after>L.TileLayer.WMTS = L.TileLayer.extend({

  defaultWmtsParams: {
    service: 'WMTS',
    request: 'GetTile',
    version: '1.0.0',
    layer: '',
    style: '',
    tilematrixSet: '',
    format: 'image/jpeg'
  },

  initialize: function(url, options) { // (String, Object)
    this._url = url;
    var wmtsParams = L.extend({}, this.defaultWmtsParams),
      tileSize = options.tileSize || this.options.tileSize;
    if (options.detectRetina && L.Browser.retina) {
      wmtsParams.width = wmtsParams.height = tileSize * 2;
    } else {
      wmtsParams.width = wmtsParams.height = tileSize;
    }
    for (var i in options) {
      // all keys that are not TileLayer options go to WMTS params
      if (!this.options.hasOwnProperty(i) && i != "matrixIds") {
        wmtsParams[i] = options[i];
      }
    }
    this.wmtsParams = wmtsParams;
    this.matrixIds = options.matrixIds || this.getDefaultMatrix();
    L.setOptions(this, options);
  },

  onAdd: function(map) {
    if (this.options.leafletObject) {
      this.options.leafletObject._map = this._map;
      if (this.getEvents) {
        var events = this.getEvents();
        map.on(events, this.options.leafletObject);
        this.once('remove', function() {
          map.off(events, this);
        }, this);
      }
      this.options.leafletObject._zoomAnimated = this._zoomAnimated;
      this.options.leafletObject._eventParents = this._eventParents;
      this.options.leafletObject._layers = this._layers;
      this.options.leafletObject._mapToAdd = this._mapToAdd;
      L.TileLayer.prototype.onAdd.call(this.options.leafletObject, map);
    } else {
      L.TileLayer.prototype.onAdd.call(this, map);
    }
  },

  getTileUrl: function(tilePoint, zoom) { // (Point, Number) -> String
    var map = this._map;
    crs = map.options.crs;
    tileSize = this.options.tileSize;
    nwPoint = tilePoint.multiplyBy(tileSize);
    //+/-1 in order to be on the tile
    nwPoint.x += 1;
    nwPoint.y -= 1;
    sePoint = nwPoint.add(new L.Point(tileSize, tileSize));
    nw = crs.project(map.unproject(nwPoint, zoom));
    se = crs.project(map.unproject(sePoint, zoom));
    tilewidth = se.x - nw.x;
    zoom = map.getZoom();
    ident = this.matrixIds[zoom].identifier;
    X0 = this.matrixIds[zoom].topLeftCorner.lng;
    Y0 = this.matrixIds[zoom].topLeftCorner.lat;
    tilecol = Math.floor((nw.x - X0) / tilewidth);
    tilerow = -Math.floor((nw.y - Y0) / tilewidth);
    // hack to fix bad tile location on north pole area
    tilerow = (Math.abs(tilerow) < 0.001)?0:tilerow;
    url = L.Util.template(this._url, {
      s: this._getSubdomain(tilePoint)
    });
    if (this.options.useProxy) {
         url  = emp.util.config.getProxyUrl() + "?" + "mime=image/*&url=" + encodeURIComponent(url);
     }
    return url + L.Util.getParamString(this.wmtsParams, url) + "&tilematrix=" + ident + "&tilerow=" + tilerow + "&tilecol=" + tilecol;
  },

  setParams: function(params, noRedraw) {
    L.extend(this.wmtsParams, params);
    if (!noRedraw) {
      this.redraw();
    }
    return this;
  },

  getDefaultMatrix: function() {
    /**
     * the matrix3857 represents the projection
     * for in the IGN WMTS for the google coordinates.
     */
    var matrixIds3857 = new Array(22);
    for (var i = 0; i < 22; i++) {
      matrixIds3857[i] = {
        identifier: "" + i,
        topLeftCorner: new L.LatLng(20037508.3428, -20037508.3428)
      };
    }
    return matrixIds3857;
  }
});

L.tileLayer.wmts = function(url, options) {
  return new L.TileLayer.WMTS(url, options);
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * Copyright 2014 Netsend.
 *
 * This file is part of Mastersync.
 *
 * Mastersync is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * Mastersync is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with Mastersync. If not, see <https://www.gnu.org/licenses/>.
 */

'use strict';

var _db = require('./_db');
var mongodb = require('mongodb');
var async = require('async');
var VersionedCollection = require('../lib/versioned_collection');
var properties = require('properties');
var fs = require('fs');

var program = require('commander');

program
  .version('0.1.0')
  .usage('[-f config] [-n] -d database -c collection')
  .description('tail the given collection')
  .option('-d, --database <database>', 'name of the database')
  .option('-c, --collection <collection>', 'name of the collection')
  .option('    --id <id>', 'show the log of one string based id')
  .option('    --oid <id>', 'show the log of one object id')
  .option('-f, --config <config>', 'ini config file with database access credentials')
  .option('-s, --show', 'show complete objects')
  .option('    --ack', 'only show versions that are ackd')
  .option('    --nack', 'only show versions that are not ackd')
  .option('-p, --patch', 'show patch compared to previous version')
  .option('    --pe <perspective>', 'perspective')
  .option('-n, --number <number>', 'number of latest versions to show, defaults to 10, 0 means unlimited')
  .parse(process.argv);

if (!program.database) { program.help(); }
if (!program.collection) { program.help(); }

var config = {};
var dbCfg = { dbName: program.database };

// if relative, prepend current working dir
if (program.config) {
  config = program.config;
  if (config[0] !== '/') {
    config = process.cwd() + '/' + config;
  }

  config = properties.parse(fs.readFileSync(config, { encoding: 'utf8' }), { sections: true, namespaces: true });

  if (config.database) {
    dbCfg = {
      dbName: program.database,
      dbHost: config.database.path || config.database.host,
      dbPort: config.database.port,
      dbUser: config.database.user,
      dbPass: config.database.pass,
      authDb: config.database.authDb
    };
  }
}

if (program.ack && program.nack) {
  console.error('error: --ack and --nack are mutally exclusive');
  process.exit(1);
}

if (program.number === '0') {
  program.number = 0;
} else {
  program.number = Number(program.number) || 10;
}

var id;
if (program.id) {
  id = program.id;
} else if (program.oid) {
  id = new mongodb.ObjectID(program.oid);
}

// shows old values of deleted and changed attributes
function fmtPatch(patch, oldItem) {
  var result = {};
  Object.keys(patch).forEach(function(key) {
    if (patch[key] !== '+') {
      result[key] = oldItem[key];
    }
  });
  return JSON.stringify(result);
}

function fmtItem(item, parents) {
  parents = parents || [];

  var out = '';
  if (program.show) {
    // print tree
    if (parents.length) {
      out += '\u250f ';
    } else {
      out += '\u257a ';
    }
    out += ' ' + JSON.stringify(item);
    if (parents.length) {
      out += '\n';
    }

    var i = 0;
    parents.forEach(function(p) {
      i++;
      if (i === parents.length) {
        out += '\u2517\u2501 ';
      } else {
        out += '\u2523\u2501 ';
      }
      var diff = VersionedCollection.diff(item, p);
      delete diff._id;
      out += ' ' + p._id._id + ' ' + p._id._v + ' diff: ' + JSON.stringify(diff) + ' ' + fmtPatch(diff, p);
      if (i !== parents.length) {
        out += '\n';
      }
    });
  } else {
    out += item._id._id;
    out += ' ' + item._id._v;
    if (item._id._lo) {
      out += ' l';
    } else {
      out += ' .';
    }
    if (item._m3.hasOwnProperty('_ack') && item._m3._ack) {
      out += ' a';
    } else {
      out += ' .';
    }
    if (item._id.hasOwnProperty('_d') && item._id._d) {
      out += ' d';
    } else {
      out += ' .';
    }
    out += ' ' + item._id._pe;
    if (item._id.hasOwnProperty('_i')) {
      out += ' (' + item._id._i + ')';
    }
    out += ' ' + JSON.stringify(item._id._pa);
    if (program.patch && parents.length) {
      parents.forEach(function(p) {
        var diff = VersionedCollection.diff(item, p);
        delete diff._id;
        if (parents.length > 1) {
          out += ' ' + p._id._v;
        }
        out += ' diff: ' + JSON.stringify(diff) + ' ' + fmtPatch(diff, p);
      });
    }
  }
  return out;
}

function run(db) {
  var coll = db.db(program.database).collection('m3.'+program.collection);

  var counter = 0;

  // start at the end
  var opts = { sort: { $natural: -1 }, comment: 'mmlog' };
  if (program.number) {
    opts.limit = program.number;
  }

  // build selector in parts to maintain an index friendly order, if possible
  var selector = { '_id._co': program.collection };
  if (id) { selector['_id._id'] = id; }
  if (program.pe) {
    selector['_id._pe'] = program.pe;
  }

  if (program.ack) { selector['_m3._ack'] = true; }
  if (program.nack) { selector['_m3._ack'] = false; }

  var stream = coll.find(selector, opts).stream();
  var error;

  stream.on('data', function(item) {
    counter++;

    if (program.patch && item._id._pa.length) {
      stream.pause();

      var parents = [];
      async.eachSeries(item._id._pa, function(pa, cb) {
        selector = { '_id._co': item._id._co, '_id._id': item._id._id, '_id._v': pa, '_id._pe': item._id._pe };
        coll.findOne(selector, function(err, p) {
          if (err) { return cb(err); }
          if (!p) {
            error = new Error('parent not found');
            console.error(error, JSON.stringify(selector));
            return cb(error);
          }

          parents.push(p);
          cb();
        });
      }, function(err) {
        if (err) {
          console.error(err, JSON.stringify(item));
          process.exit(1);
        }

        console.log(fmtItem(item, parents));

        if (counter === program.number) {
          stream.destroy();
        } else {
          stream.resume();
        }
      });
    } else {
      console.log(fmtItem(item));

      if (counter === program.number) {
        stream.destroy();
      }
    }
  });
  stream.on('error', function() {
    process.exit(1);
  });
  stream.on('close', function() {
    process.exit();
  });
}

// open database
_db(dbCfg, function(err, db) {
  if (err) { throw err; }
  run(db);
});

<commit_msg>mslog.js: add --json option<commit_after>#!/usr/bin/env node

/**
 * Copyright 2014 Netsend.
 *
 * This file is part of Mastersync.
 *
 * Mastersync is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * Mastersync is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with Mastersync. If not, see <https://www.gnu.org/licenses/>.
 */

'use strict';

var _db = require('./_db');
var mongodb = require('mongodb');
var async = require('async');
var VersionedCollection = require('../lib/versioned_collection');
var properties = require('properties');
var fs = require('fs');

var program = require('commander');

program
  .version('0.1.0')
  .usage('[-f config] [-n] -d database -c collection')
  .description('tail the given collection')
  .option('-d, --database <database>', 'name of the database')
  .option('-c, --collection <collection>', 'name of the collection')
  .option('    --id <id>', 'show the log of one string based id')
  .option('    --oid <id>', 'show the log of one object id')
  .option('-f, --config <config>', 'ini config file with database access credentials')
  .option('-s, --show', 'show complete objects')
  .option('    --json', 'format output as one json object per line (only with --show)')
  .option('    --ack', 'only show versions that are ackd')
  .option('    --nack', 'only show versions that are not ackd')
  .option('-p, --patch', 'show patch compared to previous version')
  .option('    --pe <perspective>', 'perspective')
  .option('-n, --number <number>', 'number of latest versions to show, defaults to 10, 0 means unlimited')
  .parse(process.argv);

if (!program.database) { program.help(); }
if (!program.collection) { program.help(); }

var config = {};
var dbCfg = { dbName: program.database };

// if relative, prepend current working dir
if (program.config) {
  config = program.config;
  if (config[0] !== '/') {
    config = process.cwd() + '/' + config;
  }

  config = properties.parse(fs.readFileSync(config, { encoding: 'utf8' }), { sections: true, namespaces: true });

  if (config.database) {
    dbCfg = {
      dbName: program.database,
      dbHost: config.database.path || config.database.host,
      dbPort: config.database.port,
      dbUser: config.database.user,
      dbPass: config.database.pass,
      authDb: config.database.authDb
    };
  }
}

if (program.ack && program.nack) {
  console.error('error: --ack and --nack are mutally exclusive');
  process.exit(1);
}

if (program.number === '0') {
  program.number = 0;
} else {
  program.number = Number(program.number) || 10;
}

var id;
if (program.id) {
  id = program.id;
} else if (program.oid) {
  id = new mongodb.ObjectID(program.oid);
}

// shows old values of deleted and changed attributes
function fmtPatch(patch, oldItem) {
  var result = {};
  Object.keys(patch).forEach(function(key) {
    if (patch[key] !== '+') {
      result[key] = oldItem[key];
    }
  });
  return JSON.stringify(result);
}

function fmtItem(item, parents) {
  parents = parents || [];

  var out = '';
  if (program.show) {
    // print tree
    if (!program.json) {
      if (parents.length) {
        out += '\u250f ';
      } else {
        out += '\u257a ';
      }
      out += ' ';
    }
    out += JSON.stringify(item);
    if (parents.length) {
      out += '\n';
    }

    if (!program.json) {
      var i = 0;
      parents.forEach(function(p) {
        i++;
        if (i === parents.length) {
          out += '\u2517\u2501 ';
        } else {
          out += '\u2523\u2501 ';
        }
        var diff = VersionedCollection.diff(item, p);
        delete diff._id;
        out += ' ' + p._id._id + ' ' + p._id._v + ' diff: ' + JSON.stringify(diff) + ' ' + fmtPatch(diff, p);
        if (i !== parents.length) {
          out += '\n';
        }
      });
    }
  } else {
    out += item._id._id;
    out += ' ' + item._id._v;
    if (item._id._lo) {
      out += ' l';
    } else {
      out += ' .';
    }
    if (item._m3.hasOwnProperty('_ack') && item._m3._ack) {
      out += ' a';
    } else {
      out += ' .';
    }
    if (item._id.hasOwnProperty('_d') && item._id._d) {
      out += ' d';
    } else {
      out += ' .';
    }
    out += ' ' + item._id._pe;
    if (item._id.hasOwnProperty('_i')) {
      out += ' (' + item._id._i + ')';
    }
    out += ' ' + JSON.stringify(item._id._pa);
    if (program.patch && parents.length) {
      parents.forEach(function(p) {
        var diff = VersionedCollection.diff(item, p);
        delete diff._id;
        if (parents.length > 1) {
          out += ' ' + p._id._v;
        }
        out += ' diff: ' + JSON.stringify(diff) + ' ' + fmtPatch(diff, p);
      });
    }
  }
  return out;
}

function run(db) {
  var coll = db.db(program.database).collection('m3.'+program.collection);

  var counter = 0;

  // start at the end
  var opts = { sort: { $natural: -1 }, comment: 'mmlog' };
  if (program.number) {
    opts.limit = program.number;
  }

  // build selector in parts to maintain an index friendly order, if possible
  var selector = { '_id._co': program.collection };
  if (id) { selector['_id._id'] = id; }
  if (program.pe) {
    selector['_id._pe'] = program.pe;
  }

  if (program.ack) { selector['_m3._ack'] = true; }
  if (program.nack) { selector['_m3._ack'] = false; }

  var stream = coll.find(selector, opts).stream();
  var error;

  stream.on('data', function(item) {
    counter++;

    if (program.patch && item._id._pa.length) {
      stream.pause();

      var parents = [];
      async.eachSeries(item._id._pa, function(pa, cb) {
        selector = { '_id._co': item._id._co, '_id._id': item._id._id, '_id._v': pa, '_id._pe': item._id._pe };
        coll.findOne(selector, function(err, p) {
          if (err) { return cb(err); }
          if (!p) {
            error = new Error('parent not found');
            console.error(error, JSON.stringify(selector));
            return cb(error);
          }

          parents.push(p);
          cb();
        });
      }, function(err) {
        if (err) {
          console.error(err, JSON.stringify(item));
          process.exit(1);
        }

        console.log(fmtItem(item, parents));

        if (counter === program.number) {
          stream.destroy();
        } else {
          stream.resume();
        }
      });
    } else {
      console.log(fmtItem(item));

      if (counter === program.number) {
        stream.destroy();
      }
    }
  });
  stream.on('error', function() {
    process.exit(1);
  });
  stream.on('close', function() {
    process.exit();
  });
}

// open database
_db(dbCfg, function(err, db) {
  if (err) { throw err; }
  run(db);
});

<|endoftext|>