<commit_before>let fs      = require('fs'),
    path    = require('path'),
    util    = require('util'),
    Sandbox = require("sandbox");

// With following assume, [data] folder will place all data files
// +- data
//   |- cases          * all use cases to be running
//     |- 1.in         * input file for case 1
//     |- 1.out        * expect output for case 1
//   |- source.js      * the source code of solution
//   |- result.json    * the result (score and outputs)

let defaultTimeout = 2000,
    datadir        = process.env.BASEDATA || '/data',
    casedir        = datadir + '/cases',
    caseInPostfix  = '.in',
    caseOutPostfix = '.out',
    final          = {score: 0, count: 0, results: [], timestamp: new Date().getTime()};

function formatDate(date) {
    let hours       = date.getHours(),
        minutes     = date.getMinutes(),
        seconds     = date.getSeconds(),
        millisecond = date.getMilliseconds();
    return `[${hours}:${minutes}:${seconds}.${millisecond}]`;
}

try {
    // clean old result
    fs.writeFileSync(datadir + '/result.json', JSON.stringify(final), 'utf8');

    let source = fs.readFileSync(datadir + '/source.js', 'utf8'),
        cases  = (files => files.map(file => path.join(casedir, file))
            .filter(file => path.extname(file) === caseInPostfix && fs.statSync(file).isFile())
            .map(file => {
                let caseId = path.basename(file, caseInPostfix);
                return {
                    id    : parseInt(caseId, 10),
                    input : readFileContent(file),
                    expect: readExpectFile(caseId)
                };
            })
            .sort((cs1, cs2) => cs1.id - cs2.id))(fs.readdirSync(casedir));

    runCaseAsync(cases, source, () => {
        process.stdout.write(`${formatDate(new Date())} calc final score, write result to file\n`);
        final.score = final.count * final.count;
        fs.writeFileSync(datadir + '/result.json', JSON.stringify(final), 'utf8');
    });
} catch (any) {
    process.stderr.write(`${formatDate(new Date())} Internal error: ${any} \n`);
    return;
}

function runCaseAsync(cases, source, next) {
    let cs   = cases.shift(),
        csId = cs.id;
    process.stdout.write(`${formatDate(new Date())} run case [id: ${csId}], left ${cases.length} in pending\n`);
    let sb     = new Sandbox();
    sb.timeout = defaultTimeout;
    sb.run(source + '; __proc__("' + cs.input.replace(/\n/g, '\\n') + '");',
        output => {
            output.id    = csId;
            let expected = cs.expect,
                actual   = output.result;
            process.stdout.write(`${formatDate(new Date())} comparing expected and actual for case [id: ${csId}]: \n`);
            process.stdout.write(`${formatDate(new Date())} expected=${expected}\n`);
            process.stdout.write(`${formatDate(new Date())} actual  =${actual}\n`);
            // first compare all original output
            if (expected !== actual) {
                // second deep trim all blank and newline
                expected = deepTrim(expected);
                actual   = deepTrim(actual);

                if (expected !== actual) {
                    // third wrap data if result is string
                    expected = util.inspect(cs.expect);
                    actual   = output.result;

                    if (expected !== actual) {
                        // fourth deep trim again
                        expected = deepTrim(expected);
                        actual   = deepTrim(actual);
                    }
                }
            }
            if (expected === actual) {
                output.passed = true;
                final.count += 1;
            }
            final.results[csId - 1] = output;

            if (cases.length > 0) {
                runCaseAsync(cases, source, next);
            } else {
                next();
            }
        });
}

function readFileContent(file) {
    return fs.readFileSync(file, 'utf8');
}

function readExpectFile(caseId) {
    return readFileContent(path.join(casedir, caseId + caseOutPostfix));
}

function deepTrim(realString) {
    realString       = realString.trim();
    let resultString = '', lastChar = '';
    for (let i = 0; i < realString.length; i++) {
        let currentChar = realString[i];
        if ((lastChar === ' ' || lastChar === '\u00A0') &&
            (currentChar === ' ' || currentChar === '\u00A0')) {
            continue;
        }
        resultString += currentChar;
        lastChar = currentChar;
    }
    return resultString;
}<commit_msg>fix timeout option is not applied into execution (again)<commit_after>let fs      = require('fs'),
    path    = require('path'),
    util    = require('util'),
    Sandbox = require("sandbox");

// With following assume, [data] folder will place all data files
// +- data
//   |- cases          * all use cases to be running
//     |- 1.in         * input file for case 1
//     |- 1.out        * expect output for case 1
//   |- source.js      * the source code of solution
//   |- result.json    * the result (score and outputs)

let defaultTimeout = 2000,
    datadir        = process.env.BASEDATA || '/data',
    casedir        = datadir + '/cases',
    caseInPostfix  = '.in',
    caseOutPostfix = '.out',
    final          = {score: 0, count: 0, results: [], timestamp: new Date().getTime()};

function formatDate(date) {
    let hours       = date.getHours(),
        minutes     = date.getMinutes(),
        seconds     = date.getSeconds(),
        millisecond = date.getMilliseconds();
    return `[${hours}:${minutes}:${seconds}.${millisecond}]`;
}

try {
    // clean old result
    fs.writeFileSync(datadir + '/result.json', JSON.stringify(final), 'utf8');

    let source = fs.readFileSync(datadir + '/source.js', 'utf8'),
        cases  = (files => files.map(file => path.join(casedir, file))
            .filter(file => path.extname(file) === caseInPostfix && fs.statSync(file).isFile())
            .map(file => {
                let caseId = path.basename(file, caseInPostfix);
                return {
                    id    : parseInt(caseId, 10),
                    input : readFileContent(file),
                    expect: readExpectFile(caseId)
                };
            })
            .sort((cs1, cs2) => cs1.id - cs2.id))(fs.readdirSync(casedir));

    runCaseAsync(cases, source, () => {
        process.stdout.write(`${formatDate(new Date())} calc final score, write result to file\n`);
        final.score = final.count * final.count;
        fs.writeFileSync(datadir + '/result.json', JSON.stringify(final), 'utf8');
    });
} catch (any) {
    process.stderr.write(`${formatDate(new Date())} Internal error: ${any} \n`);
    return;
}

function runCaseAsync(cases, source, next) {
    let cs   = cases.shift(),
        csId = cs.id;
    process.stdout.write(`${formatDate(new Date())} run case [id: ${csId}], left ${cases.length} in pending\n`);
    let sb     = new Sandbox();
    sb.options.timeout = defaultTimeout;
    sb.run(source + '; __proc__("' + cs.input.replace(/\n/g, '\\n') + '");',
        output => {
            output.id    = csId;
            let expected = cs.expect,
                actual   = output.result;
            process.stdout.write(`${formatDate(new Date())} comparing expected and actual for case [id: ${csId}]: \n`);
            process.stdout.write(`${formatDate(new Date())} expected=${expected}\n`);
            process.stdout.write(`${formatDate(new Date())} actual  =${actual}\n`);
            // first compare all original output
            if (expected !== actual) {
                // second deep trim all blank and newline
                expected = deepTrim(expected);
                actual   = deepTrim(actual);

                if (expected !== actual) {
                    // third wrap data if result is string
                    expected = util.inspect(cs.expect);
                    actual   = output.result;

                    if (expected !== actual) {
                        // fourth deep trim again
                        expected = deepTrim(expected);
                        actual   = deepTrim(actual);
                    }
                }
            }
            if (expected === actual) {
                output.passed = true;
                final.count += 1;
            }
            final.results[csId - 1] = output;

            if (cases.length > 0) {
                runCaseAsync(cases, source, next);
            } else {
                next();
            }
        });
}

function readFileContent(file) {
    return fs.readFileSync(file, 'utf8');
}

function readExpectFile(caseId) {
    return readFileContent(path.join(casedir, caseId + caseOutPostfix));
}

function deepTrim(realString) {
    realString       = realString.trim();
    let resultString = '', lastChar = '';
    for (let i = 0; i < realString.length; i++) {
        let currentChar = realString[i];
        if ((lastChar === ' ' || lastChar === '\u00A0') &&
            (currentChar === ' ' || currentChar === '\u00A0')) {
            continue;
        }
        resultString += currentChar;
        lastChar = currentChar;
    }
    return resultString;
}<|endoftext|>


<commit_before>import _ from "lodash";
import utils from "../utils";
/**
 * Created by steve on 11/09/15.
 */
jest.dontMock("../utils");
jest.dontMock("lodash");

describe("utils", () => {
    it("gets defaults from schema and form", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                },
                overEighteen: {
                    title: "Are you over 18 years old?",
                    type: "boolean",
                    default: false
                },
                attributes: {
                    type: "object",
                    required: ["eyecolor"],
                    properties: {
                        eyecolor: { type: "string", title: "Eye color" },
                        haircolor: { type: "string", title: "Hair color" },
                        shoulders: {
                            type: "object",
                            title: "Shoulders",
                            properties: {
                                left: { type: "string" },
                                right: { type: "string" }
                            }
                        }
                    }
                }
            }
        };

        const form = [
            {
                title: "Name",
                description: "Gimme yea name lad",
                schema: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                key: ["name"],
                type: "text"
            },
            {
                title: "Choose",
                schema: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                },
                key: ["gender"],
                type: "select",
                titleMap: [
                    {
                        name: "undefined",
                        value: "undefined"
                    },
                    {
                        name: "null",
                        value: "null"
                    },
                    {
                        name: "NaN",
                        value: "NaN"
                    }
                ]
            },
            {
                title: "Are you over 18 years old?",
                schema: {
                    title: "Are you over 18 years old?",
                    type: "boolean",
                    default: false
                },
                key: ["overEighteen"],
                type: "checkbox"
            },
            {
                title: "attributes",
                schema: {
                    type: "object",
                    required: ["eyecolor"],
                    properties: {
                        eyecolor: {
                            type: "string",
                            title: "Eye color"
                        },
                        haircolor: {
                            type: "string",
                            title: "Hair color"
                        },
                        shoulders: {
                            type: "object",
                            title: "Shoulders",
                            properties: {
                                left: {
                                    type: "string"
                                },
                                right: {
                                    type: "string"
                                }
                            }
                        }
                    }
                },
                type: "fieldset",
                items: [
                    {
                        title: "Eye color",
                        required: true,
                        schema: {
                            type: "string",
                            title: "Eye color"
                        },
                        key: ["attributes", "eyecolor"],
                        type: "text"
                    },
                    {
                        title: "Hair color",
                        schema: {
                            type: "string",
                            title: "Hair color"
                        },
                        key: ["attributes", "haircolor"],
                        type: "text"
                    },
                    {
                        title: "Shoulders",
                        schema: {
                            type: "object",
                            title: "Shoulders",
                            properties: {
                                left: {
                                    type: "string"
                                },
                                right: {
                                    type: "string"
                                }
                            }
                        },
                        type: "fieldset",
                        items: [
                            {
                                title: "left",
                                schema: {
                                    type: "string"
                                },
                                key: ["attributes", "shoulders", "left"],
                                type: "text"
                            },
                            {
                                title: "right",
                                schema: {
                                    type: "string"
                                },
                                key: ["attributes", "shoulders", "right"],
                                type: "text"
                            }
                        ]
                    }
                ]
            }
        ];
        const f = utils.getDefaults(schema);
        expect(f.form).toEqual(form);
    });

    it("should handle global defaults", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                }
            }
        };

        const form = [
            {
                title: "Name",
                description: "Gimme yea name lad",
                schema: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                foo: "bar",
                key: ["name"],
                type: "text"
            }
        ];

        const f = utils.getDefaults(
            schema,
            {},
            { formDefaults: { foo: "bar" } }
        );
        expect(f.form).toEqual(form);
    });

    it("should handle x-schema-form defaults", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string",
                    "x-schema-form": {
                        type: "textarea"
                    }
                }
            }
        };
        const f = utils.getDefaults(schema, {});
        expect(f.form[0].type).toEqual("textarea");
    });

    it("should ignore parts of schema in ignore list", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                }
            }
        };

        // no form is implicitly ['*']
        const defaults = utils.getDefaults(schema).form;
        expect(utils.merge(schema, ["*"], { gender: true })).toEqual([
            defaults[0]
        ]);
    });

    it("merges schema with different forms", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                }
            }
        };

        // no form is implicitly ['*']
        const defaults = utils.getDefaults(schema).form;
        expect(utils.merge(schema)).toEqual(defaults);
        expect(utils.merge(schema, ["*"])).toEqual(defaults);
        expect(utils.merge(schema, ["*", { type: "fieldset" }])).toEqual(
            defaults.concat([{ type: "fieldset" }])
        );

        // simple form
        expect(utils.merge(schema, ["gender"])).toEqual([defaults[1]]);
        expect(utils.merge(schema, ["gender", "name"])).toEqual([
            defaults[1],
            defaults[0]
        ]);

        // change it up
        const f = _.cloneDeep(defaults[0]);
        f.title = "Foobar";
        f.type = "password";
        expect(
            utils.merge(schema, [
                { key: "name", title: "Foobar", type: "password" }
            ])
        ).toEqual([f]);
    });

    it("should translate readOnly in schema to readonly on the merged form defintion", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    readOnly: true,
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                }
            }
        };

        const merged = utils.merge(schema, ["gender"]);
        expect(merged[0].readonly).toEqual(true);
    });

    it("should push readOnly in schema down into objects and arrays", () => {
        const schema = {
            type: "object",
            readOnly: true,
            properties: {
                sub: {
                    type: "object",
                    properties: {
                        array: {
                            type: "array",
                            items: {
                                type: "object",
                                properties: {
                                    foo: {
                                        type: "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };

        const merged = utils.merge(schema, ["*"]);

        // sub
        expect(merged[0].readonly).toEqual(true);

        // array
        expect(merged[0].items[0].readonly).toEqual(true);

        // array items
        expect(merged[0].items[0].items[0].readonly).toEqual(true);
    });

    it("should push readonly in form def down into objects and arrays", () => {
        const schema = {
            type: "object",
            properties: {
                sub: {
                    type: "object",
                    properties: {
                        array: {
                            type: "array",
                            items: {
                                type: "object",
                                properties: {
                                    foo: {
                                        type: "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };

        const merged = utils.merge(schema, [{ key: "sub", readonly: true }]);

        // sub
        expect(merged[0].readonly).toEqual(true);

        // array
        expect(merged[0].items[0].readonly).toEqual(true);

        // array items
        expect(merged[0].items[0].items[0].readonly).toEqual(true);
    });

    xit("should select and set into objects and arrays", () => {
        const schema = {
            key: ["comments"],
            add: "New",
            style: {
                add: "btn-success"
            },
            items: [
                {
                    key: ["comments", "", "name"],
                    title: "Name",
                    required: true,
                    schema: {
                        title: "Name",
                        type: "string"
                    },
                    type: "text"
                },
                {
                    key: ["comments", "", "email"],
                    title: "Email",
                    description: "Email will be used for evil.",
                    schema: {
                        title: "Email",
                        type: "string",
                        pattern: "^\\S+@\\S+$",
                        description: "Email will be used for evil."
                    },
                    type: "text"
                },
                {
                    key: ["comments", "", "spam"],
                    type: "checkbox",
                    title: "Yes I want spam.",
                    condition: "model.comments[arrayIndex].email",
                    schema: {
                        title: "Spam",
                        type: "boolean",
                        default: true
                    }
                },
                {
                    key: ["comments", "", "comment"],
                    type: "textarea",
                    title: "Comment",
                    required: true,
                    maxlength: 20,
                    validationMessage: "Don't be greedy!",
                    schema: {
                        title: "Comment",
                        type: "string",
                        maxLength: 20,
                        validationMessage: "Don't be greedy!"
                    }
                }
            ],
            title: "comments",
            required: true,
            schema: {
                type: "array",
                maxItems: 2,
                items: {
                    type: "object",
                    properties: {
                        name: {
                            title: "Name",
                            type: "string"
                        },
                        email: {
                            title: "Email",
                            type: "string",
                            pattern: "^\\S+@\\S+$",
                            description: "Email will be used for evil."
                        },
                        spam: {
                            title: "Spam",
                            type: "boolean",
                            default: true
                        },
                        comment: {
                            title: "Comment",
                            type: "string",
                            maxLength: 20,
                            validationMessage: "Don't be greedy!"
                        }
                    },
                    required: ["name", "comment"]
                }
            },
            type: "array"
        };

        // var list = utils.selectOrSet(schema, [{key: 'sub', readonly: true}]);
        const merged = utils.merge(schema, [{ key: "sub", readonly: true }]);
        // sub
        expect(merged[0].readonly).toEqual(true);

        // array
        expect(merged[0].items[0].readonly).toEqual(true);

        // array items
        expect(merged[0].items[0].items[0].readonly).toEqual(true);
    });

    it("setValue and getValue test", () => {
        let result = {};
        result = utils.setValue(["test", "tt"], result, "HI");
        expect(result.test.tt).toBe("HI");
        expect(utils.getValue("test.tt", result)).toBe("HI");
        expect(utils.getValue(["test", "tt"], result)).toBe("HI");

        result = utils.setValue("my.test.fn", result, "Value");
        expect(result.my.test.fn).toBe("Value");
        expect(utils.getValue("my.test.fn", result)).toBe("Value");
        expect(utils.getValue(["my", "test", "fn"], result)).toBe("Value");

        result = utils.setValue("number.is", result, 1326);
        expect(result.number.is).toBe(1326);
        expect(utils.getValue("number.is", result)).toBe(1326);
        expect(utils.getValue(["number", "is"], result)).toBe(1326);

        result = utils.setValue("number.is", result, 1272);
        expect(result.number.is).toBe(1272);
        expect(utils.getValue("number.is", result)).toBe(1272);
        expect(utils.getValue(["number", "is"], result)).toBe(1272);
    });

    it("removeEmpty test", () => {
        const result = {
            test: undefined,
            value: null,
            str: "",
            nonEmptyStr: "test",
            obj: { test: undefined, str: "", value: null, obj: {} }
        };
        utils.removeEmpty(result);
        expect(result).toMatchObject({ obj: { obj: {} }, nonEmptyStr: "test" });
    });
});
<commit_msg>utils: remove getValue and setValue and change the tests to use selectOrSet<commit_after>import _ from "lodash";
import utils from "../utils";
/**
 * Created by steve on 11/09/15.
 */
jest.dontMock("../utils");
jest.dontMock("lodash");

describe("utils", () => {
    it("gets defaults from schema and form", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                },
                overEighteen: {
                    title: "Are you over 18 years old?",
                    type: "boolean",
                    default: false
                },
                attributes: {
                    type: "object",
                    required: ["eyecolor"],
                    properties: {
                        eyecolor: { type: "string", title: "Eye color" },
                        haircolor: { type: "string", title: "Hair color" },
                        shoulders: {
                            type: "object",
                            title: "Shoulders",
                            properties: {
                                left: { type: "string" },
                                right: { type: "string" }
                            }
                        }
                    }
                }
            }
        };

        const form = [
            {
                title: "Name",
                description: "Gimme yea name lad",
                schema: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                key: ["name"],
                type: "text"
            },
            {
                title: "Choose",
                schema: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                },
                key: ["gender"],
                type: "select",
                titleMap: [
                    {
                        name: "undefined",
                        value: "undefined"
                    },
                    {
                        name: "null",
                        value: "null"
                    },
                    {
                        name: "NaN",
                        value: "NaN"
                    }
                ]
            },
            {
                title: "Are you over 18 years old?",
                schema: {
                    title: "Are you over 18 years old?",
                    type: "boolean",
                    default: false
                },
                key: ["overEighteen"],
                type: "checkbox"
            },
            {
                title: "attributes",
                schema: {
                    type: "object",
                    required: ["eyecolor"],
                    properties: {
                        eyecolor: {
                            type: "string",
                            title: "Eye color"
                        },
                        haircolor: {
                            type: "string",
                            title: "Hair color"
                        },
                        shoulders: {
                            type: "object",
                            title: "Shoulders",
                            properties: {
                                left: {
                                    type: "string"
                                },
                                right: {
                                    type: "string"
                                }
                            }
                        }
                    }
                },
                type: "fieldset",
                items: [
                    {
                        title: "Eye color",
                        required: true,
                        schema: {
                            type: "string",
                            title: "Eye color"
                        },
                        key: ["attributes", "eyecolor"],
                        type: "text"
                    },
                    {
                        title: "Hair color",
                        schema: {
                            type: "string",
                            title: "Hair color"
                        },
                        key: ["attributes", "haircolor"],
                        type: "text"
                    },
                    {
                        title: "Shoulders",
                        schema: {
                            type: "object",
                            title: "Shoulders",
                            properties: {
                                left: {
                                    type: "string"
                                },
                                right: {
                                    type: "string"
                                }
                            }
                        },
                        type: "fieldset",
                        items: [
                            {
                                title: "left",
                                schema: {
                                    type: "string"
                                },
                                key: ["attributes", "shoulders", "left"],
                                type: "text"
                            },
                            {
                                title: "right",
                                schema: {
                                    type: "string"
                                },
                                key: ["attributes", "shoulders", "right"],
                                type: "text"
                            }
                        ]
                    }
                ]
            }
        ];
        const f = utils.getDefaults(schema);
        expect(f.form).toEqual(form);
    });

    it("should handle global defaults", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                }
            }
        };

        const form = [
            {
                title: "Name",
                description: "Gimme yea name lad",
                schema: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                foo: "bar",
                key: ["name"],
                type: "text"
            }
        ];

        const f = utils.getDefaults(
            schema,
            {},
            { formDefaults: { foo: "bar" } }
        );
        expect(f.form).toEqual(form);
    });

    it("should handle x-schema-form defaults", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string",
                    "x-schema-form": {
                        type: "textarea"
                    }
                }
            }
        };
        const f = utils.getDefaults(schema, {});
        expect(f.form[0].type).toEqual("textarea");
    });

    it("should ignore parts of schema in ignore list", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                }
            }
        };

        // no form is implicitly ['*']
        const defaults = utils.getDefaults(schema).form;
        expect(utils.merge(schema, ["*"], { gender: true })).toEqual([
            defaults[0]
        ]);
    });

    it("merges schema with different forms", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                }
            }
        };

        // no form is implicitly ['*']
        const defaults = utils.getDefaults(schema).form;
        expect(utils.merge(schema)).toEqual(defaults);
        expect(utils.merge(schema, ["*"])).toEqual(defaults);
        expect(utils.merge(schema, ["*", { type: "fieldset" }])).toEqual(
            defaults.concat([{ type: "fieldset" }])
        );

        // simple form
        expect(utils.merge(schema, ["gender"])).toEqual([defaults[1]]);
        expect(utils.merge(schema, ["gender", "name"])).toEqual([
            defaults[1],
            defaults[0]
        ]);

        // change it up
        const f = _.cloneDeep(defaults[0]);
        f.title = "Foobar";
        f.type = "password";
        expect(
            utils.merge(schema, [
                { key: "name", title: "Foobar", type: "password" }
            ])
        ).toEqual([f]);
    });

    it("should translate readOnly in schema to readonly on the merged form defintion", () => {
        const schema = {
            type: "object",
            properties: {
                name: {
                    title: "Name",
                    description: "Gimme yea name lad",
                    type: "string"
                },
                gender: {
                    readOnly: true,
                    title: "Choose",
                    type: "string",
                    enum: ["undefined", "null", "NaN"]
                }
            }
        };

        const merged = utils.merge(schema, ["gender"]);
        expect(merged[0].readonly).toEqual(true);
    });

    it("should push readOnly in schema down into objects and arrays", () => {
        const schema = {
            type: "object",
            readOnly: true,
            properties: {
                sub: {
                    type: "object",
                    properties: {
                        array: {
                            type: "array",
                            items: {
                                type: "object",
                                properties: {
                                    foo: {
                                        type: "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };

        const merged = utils.merge(schema, ["*"]);

        // sub
        expect(merged[0].readonly).toEqual(true);

        // array
        expect(merged[0].items[0].readonly).toEqual(true);

        // array items
        expect(merged[0].items[0].items[0].readonly).toEqual(true);
    });

    it("should push readonly in form def down into objects and arrays", () => {
        const schema = {
            type: "object",
            properties: {
                sub: {
                    type: "object",
                    properties: {
                        array: {
                            type: "array",
                            items: {
                                type: "object",
                                properties: {
                                    foo: {
                                        type: "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };

        const merged = utils.merge(schema, [{ key: "sub", readonly: true }]);

        // sub
        expect(merged[0].readonly).toEqual(true);

        // array
        expect(merged[0].items[0].readonly).toEqual(true);

        // array items
        expect(merged[0].items[0].items[0].readonly).toEqual(true);
    });

    xit("should select and set into objects and arrays", () => {
        const schema = {
            key: ["comments"],
            add: "New",
            style: {
                add: "btn-success"
            },
            items: [
                {
                    key: ["comments", "", "name"],
                    title: "Name",
                    required: true,
                    schema: {
                        title: "Name",
                        type: "string"
                    },
                    type: "text"
                },
                {
                    key: ["comments", "", "email"],
                    title: "Email",
                    description: "Email will be used for evil.",
                    schema: {
                        title: "Email",
                        type: "string",
                        pattern: "^\\S+@\\S+$",
                        description: "Email will be used for evil."
                    },
                    type: "text"
                },
                {
                    key: ["comments", "", "spam"],
                    type: "checkbox",
                    title: "Yes I want spam.",
                    condition: "model.comments[arrayIndex].email",
                    schema: {
                        title: "Spam",
                        type: "boolean",
                        default: true
                    }
                },
                {
                    key: ["comments", "", "comment"],
                    type: "textarea",
                    title: "Comment",
                    required: true,
                    maxlength: 20,
                    validationMessage: "Don't be greedy!",
                    schema: {
                        title: "Comment",
                        type: "string",
                        maxLength: 20,
                        validationMessage: "Don't be greedy!"
                    }
                }
            ],
            title: "comments",
            required: true,
            schema: {
                type: "array",
                maxItems: 2,
                items: {
                    type: "object",
                    properties: {
                        name: {
                            title: "Name",
                            type: "string"
                        },
                        email: {
                            title: "Email",
                            type: "string",
                            pattern: "^\\S+@\\S+$",
                            description: "Email will be used for evil."
                        },
                        spam: {
                            title: "Spam",
                            type: "boolean",
                            default: true
                        },
                        comment: {
                            title: "Comment",
                            type: "string",
                            maxLength: 20,
                            validationMessage: "Don't be greedy!"
                        }
                    },
                    required: ["name", "comment"]
                }
            },
            type: "array"
        };

        // var list = utils.selectOrSet(schema, [{key: 'sub', readonly: true}]);
        const merged = utils.merge(schema, [{ key: "sub", readonly: true }]);
        // sub
        expect(merged[0].readonly).toEqual(true);

        // array
        expect(merged[0].items[0].readonly).toEqual(true);

        // array items
        expect(merged[0].items[0].items[0].readonly).toEqual(true);
    });

    it("selectOrSet test", () => {
        const result = {};
        utils.selectOrSet(["test", "tt"], result, "HI");
        expect(result.test.tt).toBe("HI");
        expect(utils.selectOrSet("test.tt", result)).toBe("HI");
        expect(utils.selectOrSet(["test", "tt"], result)).toBe("HI");

        utils.selectOrSet("my.test.fn", result, "Value");
        expect(result.my.test.fn).toBe("Value");
        expect(utils.selectOrSet("my.test.fn", result)).toBe("Value");
        expect(utils.selectOrSet(["my", "test", "fn"], result)).toBe("Value");

        utils.selectOrSet("number.is", result, 1326);
        expect(result.number.is).toBe(1326);
        expect(utils.selectOrSet("number.is", result)).toBe(1326);
        expect(utils.selectOrSet(["number", "is"], result)).toBe(1326);

        utils.selectOrSet("number.is", result, 1272);
        expect(result.number.is).toBe(1272);
        expect(utils.selectOrSet("number.is", result)).toBe(1272);
        expect(utils.selectOrSet(["number", "is"], result)).toBe(1272);
    });

    it("removeEmpty test", () => {
        const result = {
            test: undefined,
            value: null,
            str: "",
            nonEmptyStr: "test",
            obj: { test: undefined, str: "", value: null, obj: {} }
        };
        utils.removeEmpty(result);
        expect(result).toMatchObject({ obj: { obj: {} }, nonEmptyStr: "test" });
    });
});
<|endoftext|>


<commit_before>/**
 * @file Calculation vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import isArray from 'lodash/isArray';
import Valid from './Valid';
import SelectMode from '../_statics/SelectMode';
import Util from './Util';

function pageSize(pageSize, pageSizes, defaultValue) {

    if (pageSize) {
        return pageSize;
    }

    if (pageSizes && pageSizes.length > 0) {

        for (let item of pageSizes) {
            if (item > 0) {
                return item;
            }
        }
    }

    return defaultValue;

}

function displayIndexByScrollTop(data, listHeight, itemHeight, scrollTop = 0, buffer = 0) {

    if (!data || !listHeight || !itemHeight) {
        return {
            start: 0,
            stop: 0,
            startWithBuffer: 0,
            stopWithBuffer: 0
        };
    }

    const len = data.length,
        start = Math.floor(scrollTop / itemHeight),
        stop = start + Math.ceil(listHeight / itemHeight);

    return {
        start: Valid.range(start, 0, len - 1),
        stop: Valid.range(stop, 0, len - 1),
        startWithBuffer: Valid.range(start - buffer, 0, len - 1),
        stopWithBuffer: Valid.range(stop + buffer, 0, len - 1)
    };

}

function displayIndexByScrollTopMulColumns(data, listHeight, itemHeight, column, scrollTop = 0, buffer = 0) {

    if (!data || !listHeight || !itemHeight) {
        return {
            start: 0,
            stop: 0,
            startWithBuffer: 0,
            stopWithBuffer: 0
        };
    }

    const len = data.length,
        start = Math.floor(scrollTop / itemHeight) * column,
        stop = start + Math.ceil(listHeight / itemHeight) * column;

    return {
        start: Valid.range(start, 0, len - 1),
        stop: Valid.range(stop, 0, len - 1),
        startWithBuffer: Valid.range(Valid.range(start, 0, len - 1) - buffer, 0, len - 1),
        stopWithBuffer: Valid.range(stop + buffer, 0, len - 1)
    };

}

function getInitValue(props) {

    if (!props) {
        return;
    }

    const {value, selectMode} = props;

    if (!selectMode) {
        return;
    }

    if (value) {
        return value;
    }

    switch (selectMode) {
        case SelectMode.MULTI_SELECT:
            return [];
        case SelectMode.SINGLE_SELECT:
            return null;
        default:
            return value;
    }

}

function getMultiSelectItemIndex(item, value, {selectMode, valueField, displayField}) {

    if (selectMode !== SelectMode.MULTI_SELECT || !item || !value) {
        return -1;
    }

    if (!isArray(value)) {
        return -1;
    }

    return value.findIndex(valueItem => Util.isValueEqual(valueItem, item, valueField, displayField));

}

function isItemChecked(item, value, {selectMode, valueField, displayField}) {

    if (!item || !value) {
        return false;
    }

    if (selectMode === SelectMode.MULTI_SELECT) {
        return isArray(value) && value.filter(valueItem =>
            Util.isValueEqual(valueItem, item, valueField, displayField)
        ).length > 0;
    } else if (selectMode === SelectMode.SINGLE_SELECT) {
        return Util.isValueEqual(value, item, valueField, displayField);
    }

}

function isNodeIndeterminate(node, value, {valueField, displayField}) {

    if (!node || !node.children || node.children.length < 1
        || !value || !value.length || value.length < 1) {
        return false;
    }

    let total = 0,
        count = 0;

    Util.preOrderTraverse(node, nodeItem => {
        total++;
        if (value.findIndex(item =>
                Util.getValueByValueField(item, valueField, displayField)
                === Util.getValueByValueField(nodeItem, valueField, displayField)) > -1) {
            count++;
        }
    });

    return count > 0 && total !== count;

}

export default {
    pageSize,
    displayIndexByScrollTop,
    getInitValue,
    getMultiSelectItemIndex,
    displayIndexByScrollTopMulColumns,
    isItemChecked,
    isNodeIndeterminate
};<commit_msg>update Calculation<commit_after>/**
 * @file Calculation vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import isArray from 'lodash/isArray';
import Valid from './Valid';
import SelectMode from '../_statics/SelectMode';
import Util from './Util';

function pageSize(pageSize, pageSizes, defaultValue) {

    if (pageSize) {
        return pageSize;
    }

    if (pageSizes && pageSizes.length > 0) {

        for (let item of pageSizes) {
            if (item > 0) {
                return item;
            }
        }
    }

    return defaultValue;

}

function displayIndexByScrollTop(data, listHeight, itemHeight, scrollTop = 0, buffer = 0) {

    if (!data || !listHeight || !itemHeight) {
        return {
            start: 0,
            stop: 0,
            startWithBuffer: 0,
            stopWithBuffer: 0
        };
    }

    const len = data.length,
        start = Math.floor(scrollTop / itemHeight),
        stop = start + Math.ceil(listHeight / itemHeight);

    return {
        start: Valid.range(start, 0, len - 1),
        stop: Valid.range(stop, 0, len - 1),
        startWithBuffer: Valid.range(start - buffer, 0, len - 1),
        stopWithBuffer: Valid.range(stop + buffer, 0, len - 1)
    };

}

function displayIndexByScrollTopMulColumns(data, listHeight, itemHeight, column, scrollTop = 0, buffer = 0) {

    if (!data || !listHeight || !itemHeight) {
        return {
            start: 0,
            stop: 0,
            startWithBuffer: 0,
            stopWithBuffer: 0
        };
    }

    const len = data.length,
        start = Math.floor(scrollTop / itemHeight) * column,
        stop = start + Math.ceil(listHeight / itemHeight) * column;

    return {
        start: Valid.range(start, 0, len - 1),
        stop: Valid.range(stop, 0, len - 1),
        startWithBuffer: Valid.range(Valid.range(start, 0, len - 1) - buffer, 0, len - 1),
        stopWithBuffer: Valid.range(stop + buffer, 0, len - 1)
    };

}

function getInitValue(props) {

    if (!props) {
        return;
    }

    const {value, selectMode} = props;

    if (!selectMode) {
        return;
    }

    if (value) {
        return value;
    }

    switch (selectMode) {
        case SelectMode.MULTI_SELECT:
            return [];
        case SelectMode.SINGLE_SELECT:
            return null;
        default:
            return value;
    }

}

function getMultiSelectItemIndex(item, value, {selectMode, valueField, displayField}) {

    if (selectMode !== SelectMode.MULTI_SELECT || !item || !value) {
        return -1;
    }

    if (!isArray(value)) {
        return -1;
    }

    return value.findIndex(valueItem => Util.isValueEqual(valueItem, item, valueField, displayField));

}

function isItemChecked(item, value, {selectMode, valueField, displayField}) {

    if (!item || !value) {
        return false;
    }

    if (selectMode === SelectMode.MULTI_SELECT) {
        return isArray(value) && value.filter(valueItem =>
            Util.isValueEqual(valueItem, item, valueField, displayField)
        ).length > 0;
    } else if (selectMode === SelectMode.SINGLE_SELECT) {
        return Util.isValueEqual(value, item, valueField, displayField);
    }

}

function isNodeIndeterminate(node, value, {valueField, displayField}) {

    if (!node || !node.children || node.children.length < 1
        || !value || !value.length || value.length < 1) {
        return false;
    }

    let total = 0,
        count = 0;

    Util.preOrderTraverse(node, nodeItem => {
        total++;
        if (value.findIndex(item =>
            Util.getValueByValueField(item, valueField, displayField)
            === Util.getValueByValueField(nodeItem, valueField, displayField)) > -1) {
            count++;
        }
    });

    return count > 0 && total !== count;

}

function filterLocalAutoCompleteData(filter, props) {

    const {data, minFilterLength} = props;

    if (!filter || filter.length < minFilterLength) {
        return data;
    }

    const {valueField, displayField, renderer, filterCallback} = props;

    if (filterCallback) {
        return filterCallback(filter, data);
    }

    return data && data.filter(item => {

        if (!item) {
            return false;
        }

        if (renderer) {
            return renderer(item).toString().toUpperCase().includes(filter.toUpperCase());
        }

        if (typeof item === 'object') {

            const itemDisplay = Util.getTextByDisplayField(item, displayField, valueField);

            if (itemDisplay) {
                return itemDisplay.toString().toUpperCase().includes(filter.toUpperCase());
            }

        }

        return item.toString().toUpperCase().includes(filter.toUpperCase());

    });

}

function sortTableData(data, sort, sortFunc) {

    if (!sort) {
        return data;
    }

    let copyData = data.slice();

    if (sortFunc) {
        copyData = sortFunc(copyData, sort);
    } else {
        copyData.sort((a, b) => {
            if (!isNaN(a[sort.prop]) && !isNaN(b[sort.prop])) {
                return (Number(a[sort.prop]) - Number(b[sort.prop])) * sort.type;
            } else {
                return (a[sort.prop] + '').localeCompare(b[sort.prop] + '') * sort.type;
            }
        });
    }

    return copyData;

};

export default {
    pageSize,
    displayIndexByScrollTop,
    getInitValue,
    getMultiSelectItemIndex,
    displayIndexByScrollTopMulColumns,
    isItemChecked,
    isNodeIndeterminate,
    filterLocalAutoCompleteData,
    sortTableData
};
<|endoftext|>


<commit_before>/**
 * @file TableLayout vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import sum from 'lodash/sum';
import addClass from 'dom-helpers/class/addClass';
import removeClass from 'dom-helpers/class/removeClass';

import TableFragment from '../_statics/TableFragment';
import HorizontalAlign from '../_statics/HorizontalAlign';
import Direction from '../_statics/Direction';
import ScrollBar from './ScrollBar';

/**
 * whether a table has a fixed head or a fixed foot or fixed left columns or fixed right columns
 * @param props
 * @param columns
 * @returns {boolean}
 */
function hasFixed(props, columns) {

    if (props && (props.isHeadFixed || props.isFootFixed)) {
        return true;
    }

    if (!columns) {
        return false;
    }

    if ((columns[HorizontalAlign.LEFT] && columns[HorizontalAlign.LEFT].length > 0)
        || (columns[HorizontalAlign.RIGHT] && columns[HorizontalAlign.RIGHT].length > 0)) {
        return true;
    }

    return false;

}

/**
 * calculate each head, body and foot column width
 * @returns {null|{[p: string]: *|*}}
 */
function getColumnsWidth(tableEl) {

    if (!tableEl) {
        return null;
    }

    return {
        [TableFragment.HEAD]: [].map.call(tableEl.querySelector('thead tr').querySelectorAll('th'),
            el => el.offsetWidth),
        [TableFragment.BODY]: [].map.call(tableEl.querySelector('tbody tr').querySelectorAll('td'),
            el => el.offsetWidth),
        [TableFragment.FOOT]: [].map.call(tableEl.querySelector('tfoot tr').querySelectorAll('td'),
            el => el.offsetWidth)
    };

}

/**
 * calculate each head, body and foot row height
 * @returns {null|{[p: string]: *|*}}
 */
function getRowsHeight(tableEl) {

    if (!tableEl) {
        return null;
    }

    return {
        [TableFragment.HEAD]: [].map.call(tableEl.querySelectorAll('thead tr'),
            el => el.offsetHeight),
        [TableFragment.BODY]: [].map.call(tableEl.querySelectorAll('tbody tr'),
            el => el.offsetHeight),
        [TableFragment.FOOT]: [].map.call(tableEl.querySelectorAll('tfoot tr'),
            el => el.offsetHeight)
    };

}

/**
 * calculate table body scroller height
 * @param headHeight
 * @param footHeight
 * @returns {{height: string}}
 */
function getbodyScollerHeight(headHeight, footHeight) {
    return `calc(100%${headHeight ? ` - ${sum(headHeight)}px` : ''}${footHeight ? ` - ${sum(footHeight)}px` : ''})`;
}

function maskCenterBody(wrapperEl, tableEl, fixedHeadHeight, fixedFootHeight) {
    const el = wrapperEl.querySelector('.table-content-center .scrollable-table-body-mask');
    if (el) {
        el.style.height = `${tableEl.offsetHeight - fixedHeadHeight - fixedFootHeight - 2}px`;
        const table = el.querySelector('table');
        if (table) {
            table.style.marginTop = `${-fixedHeadHeight - 1}px`;
        }
    }
}

function fixFragmentWidth(wrapperEl, columnsWidth, fixed, fragment, selector) {

    const el = wrapperEl.querySelector(`${selector} .scrollable-table-${fragment}-scroller`);

    if (el) {
        const cols = el.querySelectorAll('col');
        if (cols) {
            if (fixed === HorizontalAlign.RIGHT) {
                cols.forEach((el, index) => {
                    if (el) {
                        el.style.width = `${columnsWidth[fragment]
                            [columnsWidth[fragment].length - (cols.length - index)]}px`;
                    }
                });
            } else {
                cols.forEach((el, index) => {
                    if (el) {
                        el.style.width = `${columnsWidth[fragment][index]}px`;
                    }
                });
            }
        }
    }

}

function fixTableWidth(wrapperEl, columnsWidth, fixed, props) {

    if (!wrapperEl) {
        return;
    }

    const selector = `.table-content-${fixed || 'center'}`;

    /**
     * head
     */
    if (props && props.isHeadFixed) {
        fixFragmentWidth(wrapperEl, columnsWidth, fixed, TableFragment.HEAD, selector);
    }

    /**
     * body
     */
    if (fixed) {
        fixFragmentWidth(wrapperEl, columnsWidth, fixed, TableFragment.BODY, selector);
    }

    /**
     * foot
     */
    if (props && props.isHeadFixed) {
        fixFragmentWidth(wrapperEl, columnsWidth, fixed, TableFragment.FOOT, selector);
    }

}

function fixFragmentHeight(wrapperEl, rowsHeight, fixed, fragment, selector) {

    const el = wrapperEl.querySelector(`${selector} .scrollable-table-${fragment} t${fragment}`);

    if (el) {
        const trs = el.querySelectorAll('tr');
        if (trs) {
            trs.forEach((el, index) => {
                if (el) {
                    el.style.height = `${rowsHeight[fragment][index]}px`;
                }
            });
        }
    }

}

function fixTableHeight(wrapperEl, rowsHeight, fixed, props) {

    if (!wrapperEl) {
        return;
    }

    const selector = `.table-content-${fixed || 'center'}`;

    /**
     * head
     */
    if (props && props.isHeadFixed) {
        fixFragmentHeight(wrapperEl, rowsHeight, fixed, TableFragment.HEAD, selector);
    }

    /**
     * body
     */
    if (fixed) {
        fixFragmentHeight(wrapperEl, rowsHeight, fixed, TableFragment.BODY, selector);
    }

    /**
     * foot
     */
    if (props && props.isHeadFixed) {
        fixFragmentHeight(wrapperEl, rowsHeight, fixed, TableFragment.FOOT, selector);
    }

}

function handleHorizontalScrollStyle(el, shouldScroll, size = ScrollBar.getSize(Direction.HORIZONTAL)) {

    if (!el) {
        return;
    }

    if (!shouldScroll) {
        el.style.marginBottom = 0;
        el.style.paddingBottom = 0;
    } else if (size && size > 0) {
        el.style.marginBottom = `${-size}px`;
        el.style.paddingBottom = 0;
    } else {
        el.style.marginBottom = '-20px';
        el.style.paddingBottom = '20px';
    }

}

function fixTableHorizontalScroll(wrapperEl, props) {

    if (!wrapperEl) {
        return;
    }

    // no need to fix the scroll problem in mac
    const horizontalScrollBarSize = ScrollBar.getSize(Direction.HORIZONTAL);
    if (!horizontalScrollBarSize || horizontalScrollBarSize <= 0) {
        return;
    }

    const centerBody = wrapperEl.querySelector('.table-content-center .scrollable-table-body-scroller'),
        shouldScroll = centerBody.offsetWidth - horizontalScrollBarSize < centerBody.scrollWidth;

    /**
     * center head scroller
     */
    if (props && props.isHeadFixed) {
        const centerHead = wrapperEl.querySelector('.table-content-center .scrollable-table-head-scroller');
        if (centerHead) {
            handleHorizontalScrollStyle(centerHead, shouldScroll, horizontalScrollBarSize);
        }
    }

    /**
     * center foot scroller
     */
    if (props && props.isFootFixed) {
        const centerFoot = wrapperEl.querySelector('.table-content-center .scrollable-table-foot-scroller');
        if (centerFoot) {
            handleHorizontalScrollStyle(centerFoot, shouldScroll, horizontalScrollBarSize);
        }
    }

    /**
     * left body scroller
     */
    const leftBody = wrapperEl.querySelector('.table-content-left .scrollable-table-body-scroller');
    if (leftBody) {
        handleHorizontalScrollStyle(leftBody, shouldScroll, horizontalScrollBarSize);
    }

    /**
     * left body mask
     */
    const leftBodyMask = wrapperEl.querySelector('.table-content-left .scrollable-table-body-mask');
    if (leftBodyMask) {
        leftBodyMask.style.paddingBottom = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

    /**
     * left foot
     */
    const leftFoot = wrapperEl.querySelector('.table-content-left .scrollable-table-foot');
    if (leftFoot) {
        leftFoot.style.marginTop = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

    /**
     * right body scroller
     */
    const rightBody = wrapperEl.querySelector('.table-content-right .scrollable-table-body-scroller');
    if (rightBody) {
        handleHorizontalScrollStyle(rightBody, shouldScroll, horizontalScrollBarSize);
    }

    /**
     * right body mask
     */
    const rightBodyMask = wrapperEl.querySelector('.table-content-right .scrollable-table-body-mask');
    if (rightBodyMask) {
        rightBodyMask.style.paddingBottom = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

    /**
     * right foot
     */
    const rightFoot = wrapperEl.querySelector('.table-content-right .scrollable-table-foot');
    if (rightFoot) {
        rightFoot.style.marginTop = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

}

function fixTableVerticalScroll(wrapperEl, props) {

    if (!wrapperEl) {
        return;
    }

    const verticalScrollBarSize = ScrollBar.getSize(Direction.VERTICAL);

    /**
     * fix center head width
     */
    if (props && props.isHeadFixed && verticalScrollBarSize && verticalScrollBarSize > 0) {
        const centerHead = wrapperEl.querySelector('.table-content-center .scrollable-table-head-scroller');
        if (centerHead) {
            centerHead.style.width = `calc(100% - ${verticalScrollBarSize}px)`;
        }
    }

    /**
     * fix center foot width
     */
    if (props && props.isFootFixed && verticalScrollBarSize && verticalScrollBarSize > 0) {
        const centerFoot = wrapperEl.querySelector('.table-content-center .scrollable-table-foot-scroller');
        if (centerFoot) {
            centerFoot.style.width = `calc(100% - ${verticalScrollBarSize}px)`;
        }
    }

    /**
     * fix left body horizontal scroll bar
     */
    const leftBody = wrapperEl.querySelector('.table-content-left .scrollable-table-body-scroller');
    if (leftBody) {
        if (verticalScrollBarSize && verticalScrollBarSize > 0) {
            leftBody.style.marginRight = `-${verticalScrollBarSize}px`;
            leftBody.style.paddingRight = 0;
        } else {
            const leftBodyTable = leftBody.querySelector('table');
            if (leftBodyTable) {
                const restWidth = wrapperEl.offsetWidth - leftBodyTable.offsetWidth;
                leftBody.style.marginRight = `-${restWidth}px`;
                leftBody.style.paddingRight = `${restWidth}px`;
            }
        }
    }

}

function fixLayout(wrapperEl, props) {

    if (!wrapperEl) {
        return;
    }

    const tableEl = wrapperEl.querySelector('.table-content-center .scrollable-table-body table');

    if (!tableEl) {
        return;
    }

    const columnsWidth = getColumnsWidth(tableEl),
        rowsHeight = getRowsHeight(tableEl),

        fixedHeadHeight = sum(rowsHeight[TableFragment.HEAD]) || 0,
        fixedFootHeight = sum(rowsHeight[TableFragment.FOOT]) || 0;

    /**
     * fix horizontal scroll style
     */
    fixTableHorizontalScroll(wrapperEl, props);

    /**
     * center
     */
    maskCenterBody(wrapperEl, tableEl, fixedHeadHeight, fixedFootHeight);
    fixTableWidth(wrapperEl, columnsWidth, null, props);
    fixTableHeight(wrapperEl, rowsHeight, null, props);

    /**
     * left
     */
    fixTableWidth(wrapperEl, columnsWidth, HorizontalAlign.LEFT, props);
    fixTableHeight(wrapperEl, rowsHeight, HorizontalAlign.LEFT, props);

    /**
     * right
     */
    fixTableWidth(wrapperEl, columnsWidth, HorizontalAlign.RIGHT, props);
    fixTableHeight(wrapperEl, rowsHeight, HorizontalAlign.RIGHT, props);

    /**
     * fix vertical scroll style
     */
    fixTableVerticalScroll(wrapperEl, props);

}

function updateHorizontalScrollClassNames(wrapperEl, scrollerEl) {

    if (!wrapperEl || !scrollerEl) {
        return;
    }

    removeClass(wrapperEl, 'scroll-left');
    removeClass(wrapperEl, 'scroll-center');
    removeClass(wrapperEl, 'scroll-right');

    const {scrollWidth, offsetWidth, scrollLeft} = scrollerEl,
        verticalScrollBarSize = ScrollBar.getSize(Direction.VERTICAL);

    // no scroll
    if (scrollWidth === offsetWidth - verticalScrollBarSize) {
        return;
    }

    if (scrollLeft === 0) {
        addClass(wrapperEl, 'scroll-left');
    } else if (scrollLeft === scrollWidth - offsetWidth + verticalScrollBarSize) {
        addClass(wrapperEl, 'scroll-right');
    } else {
        addClass(wrapperEl, 'scroll-center');
    }

}

export default {
    hasFixed,
    getColumnsWidth,
    getRowsHeight,
    getbodyScollerHeight,
    fixLayout,
    updateHorizontalScrollClassNames
};
<commit_msg>update TableLayout<commit_after>/**
 * @file TableLayout vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import sum from 'lodash/sum';
import addClass from 'dom-helpers/class/addClass';
import removeClass from 'dom-helpers/class/removeClass';

import TableFragment from '../_statics/TableFragment';
import HorizontalAlign from '../_statics/HorizontalAlign';
import Direction from '../_statics/Direction';
import ScrollBar from './ScrollBar';

/**
 * whether a table has a fixed head or a fixed foot or fixed left columns or fixed right columns
 * @param props
 * @param columns
 * @returns {boolean}
 */
function hasFixed(props, columns) {

    if (props && (props.isHeadFixed || props.isFootFixed)) {
        return true;
    }

    if (!columns) {
        return false;
    }

    if ((columns[HorizontalAlign.LEFT] && columns[HorizontalAlign.LEFT].length > 0)
        || (columns[HorizontalAlign.RIGHT] && columns[HorizontalAlign.RIGHT].length > 0)) {
        return true;
    }

    return false;

}

/**
 * calculate each head, body and foot column width
 * @returns {null|{[p: string]: *|*}}
 */
function getColumnsWidth(tableEl) {

    if (!tableEl) {
        return null;
    }

    return {
        [TableFragment.BODY]: [].map.call(
            tableEl.querySelector('tbody tr').querySelectorAll('td'),
            td => td ? td.offsetWidth : 0
        ),
        [TableFragment.FOOT]: [].map.call(
            tableEl.querySelector('tfoot tr').querySelectorAll('td'),
            td => td ? td.offsetWidth : 0
        )
    };

}

/**
 * calculate each head, body and foot row height
 * @returns {null|{[p: string]: *|*}}
 */
function getRowsHeight(tableEl) {

    if (!tableEl) {
        return null;
    }

    return {
        [TableFragment.HEAD]: [].map.call(tableEl.querySelectorAll('thead tr'),
            el => el.offsetHeight),
        [TableFragment.BODY]: [].map.call(tableEl.querySelectorAll('tbody tr'),
            el => el.offsetHeight),
        [TableFragment.FOOT]: [].map.call(tableEl.querySelectorAll('tfoot tr'),
            el => el.offsetHeight)
    };

}

/**
 * calculate table body scroller height
 * @param headHeight
 * @param footHeight
 * @returns {{height: string}}
 */
function getbodyScollerHeight(headHeight, footHeight) {
    return `calc(100%${headHeight ? ` - ${sum(headHeight)}px` : ''}${footHeight ? ` - ${sum(footHeight)}px` : ''})`;
}

function maskCenterBody(wrapperEl, tableEl, fixedHeadHeight, fixedFootHeight) {
    const el = wrapperEl.querySelector('.table-content-center .scrollable-table-body-mask');
    if (el) {
        el.style.height = `${tableEl.offsetHeight - fixedHeadHeight - fixedFootHeight - 2}px`;
        const table = el.querySelector('table');
        if (table) {
            table.style.marginTop = `${-fixedHeadHeight - 1}px`;
        }
    }
}

function fixFragmentWidth(wrapperEl, columnsWidth, fixed, fragment, selector) {

    const el = wrapperEl.querySelector(`${selector} .scrollable-table-${fragment}-scroller`);

    if (el) {
        const cols = el.querySelectorAll('col');
        if (cols) {
            if (fixed === HorizontalAlign.RIGHT) {
                cols.forEach((el, index) => {
                    if (el) {
                        el.style.width = `${columnsWidth
                            [columnsWidth.length - (cols.length - index)]}px`;
                    }
                });
            } else {
                cols.forEach((el, index) => {
                    if (el) {
                        el.style.width = `${columnsWidth[index]}px`;
                    }
                });
            }
        }
    }

}

function fixTableWidth(wrapperEl, columnsWidth, fixed, props) {

    if (!wrapperEl) {
        return;
    }

    const selector = `.table-content-${fixed || 'center'}`;

    /**
     * head
     */
    if (props && props.isHeadFixed) {
        fixFragmentWidth(wrapperEl, columnsWidth[TableFragment.BODY], fixed, TableFragment.HEAD, selector);
    }

    /**
     * body
     */
    if (fixed) {
        fixFragmentWidth(wrapperEl, columnsWidth[TableFragment.BODY], fixed, TableFragment.BODY, selector);
    }

    /**
     * foot
     */
    if (props && props.isHeadFixed) {
        fixFragmentWidth(wrapperEl, columnsWidth[TableFragment.FOOT], fixed, TableFragment.FOOT, selector);
    }

}

function fixFragmentHeight(wrapperEl, rowsHeight, fixed, fragment, selector) {

    const el = wrapperEl.querySelector(`${selector} .scrollable-table-${fragment} t${fragment}`);

    if (el) {
        const trs = el.querySelectorAll('tr');
        if (trs) {
            trs.forEach((el, index) => {
                if (el) {
                    el.style.height = `${rowsHeight[fragment][index]}px`;
                }
            });
        }
    }

}

function fixTableHeight(wrapperEl, rowsHeight, fixed, props) {

    if (!wrapperEl) {
        return;
    }

    const selector = `.table-content-${fixed || 'center'}`;

    /**
     * head
     */
    if (props && props.isHeadFixed) {
        fixFragmentHeight(wrapperEl, rowsHeight, fixed, TableFragment.HEAD, selector);
    }

    /**
     * body
     */
    if (fixed) {
        fixFragmentHeight(wrapperEl, rowsHeight, fixed, TableFragment.BODY, selector);
    }

    /**
     * foot
     */
    if (props && props.isHeadFixed) {
        fixFragmentHeight(wrapperEl, rowsHeight, fixed, TableFragment.FOOT, selector);
    }

}

function handleHorizontalScrollStyle(el, shouldScroll, size = ScrollBar.getSize(Direction.HORIZONTAL)) {

    if (!el) {
        return;
    }

    if (!shouldScroll) {
        el.style.marginBottom = 0;
        el.style.paddingBottom = 0;
    } else if (size && size > 0) {
        el.style.marginBottom = `${-size}px`;
        el.style.paddingBottom = 0;
    } else {
        el.style.marginBottom = '-20px';
        el.style.paddingBottom = '20px';
    }

}

function fixTableHorizontalScroll(wrapperEl, props) {

    if (!wrapperEl) {
        return;
    }

    // no need to fix the scroll problem in mac
    const horizontalScrollBarSize = ScrollBar.getSize(Direction.HORIZONTAL);
    if (!horizontalScrollBarSize || horizontalScrollBarSize <= 0) {
        return;
    }

    const centerBody = wrapperEl.querySelector('.table-content-center .scrollable-table-body-scroller'),
        shouldScroll = centerBody.offsetWidth - horizontalScrollBarSize < centerBody.scrollWidth;

    /**
     * center head scroller
     */
    if (props && props.isHeadFixed) {
        const centerHead = wrapperEl.querySelector('.table-content-center .scrollable-table-head-scroller');
        if (centerHead) {
            handleHorizontalScrollStyle(centerHead, shouldScroll, horizontalScrollBarSize);
        }
    }

    /**
     * center foot scroller
     */
    if (props && props.isFootFixed) {
        const centerFoot = wrapperEl.querySelector('.table-content-center .scrollable-table-foot-scroller');
        if (centerFoot) {
            handleHorizontalScrollStyle(centerFoot, shouldScroll, horizontalScrollBarSize);
        }
    }

    /**
     * left body scroller
     */
    const leftBody = wrapperEl.querySelector('.table-content-left .scrollable-table-body-scroller');
    if (leftBody) {
        handleHorizontalScrollStyle(leftBody, shouldScroll, horizontalScrollBarSize);
    }

    /**
     * left body mask
     */
    const leftBodyMask = wrapperEl.querySelector('.table-content-left .scrollable-table-body-mask');
    if (leftBodyMask) {
        leftBodyMask.style.paddingBottom = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

    /**
     * left foot
     */
    const leftFoot = wrapperEl.querySelector('.table-content-left .scrollable-table-foot');
    if (leftFoot) {
        leftFoot.style.marginTop = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

    /**
     * right body scroller
     */
    const rightBody = wrapperEl.querySelector('.table-content-right .scrollable-table-body-scroller');
    if (rightBody) {
        handleHorizontalScrollStyle(rightBody, shouldScroll, horizontalScrollBarSize);
    }

    /**
     * right body mask
     */
    const rightBodyMask = wrapperEl.querySelector('.table-content-right .scrollable-table-body-mask');
    if (rightBodyMask) {
        rightBodyMask.style.paddingBottom = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

    /**
     * right foot
     */
    const rightFoot = wrapperEl.querySelector('.table-content-right .scrollable-table-foot');
    if (rightFoot) {
        rightFoot.style.marginTop = shouldScroll ? `${horizontalScrollBarSize}px` : 0;
    }

}

function fixTableVerticalScroll(wrapperEl, props) {

    if (!wrapperEl) {
        return;
    }

    const verticalScrollBarSize = ScrollBar.getSize(Direction.VERTICAL);

    /**
     * fix center head width
     */
    if (props && props.isHeadFixed && verticalScrollBarSize && verticalScrollBarSize > 0) {
        const centerHead = wrapperEl.querySelector('.table-content-center .scrollable-table-head-scroller');
        if (centerHead) {
            centerHead.style.width = `calc(100% - ${verticalScrollBarSize}px)`;
        }
    }

    /**
     * fix center foot width
     */
    if (props && props.isFootFixed && verticalScrollBarSize && verticalScrollBarSize > 0) {
        const centerFoot = wrapperEl.querySelector('.table-content-center .scrollable-table-foot-scroller');
        if (centerFoot) {
            centerFoot.style.width = `calc(100% - ${verticalScrollBarSize}px)`;
        }
    }

    /**
     * fix left body horizontal scroll bar
     */
    const leftBody = wrapperEl.querySelector('.table-content-left .scrollable-table-body-scroller');
    if (leftBody) {
        if (verticalScrollBarSize && verticalScrollBarSize > 0) {
            leftBody.style.marginRight = `-${verticalScrollBarSize}px`;
            leftBody.style.paddingRight = 0;
        } else {
            const leftBodyTable = leftBody.querySelector('table');
            if (leftBodyTable) {
                const restWidth = wrapperEl.offsetWidth - leftBodyTable.offsetWidth;
                leftBody.style.marginRight = `-${restWidth}px`;
                leftBody.style.paddingRight = `${restWidth}px`;
            }
        }
    }

}

function fixLayout(wrapperEl, props) {

    if (!wrapperEl) {
        return;
    }

    const tableEl = wrapperEl.querySelector('.table-content-center .scrollable-table-body table');

    if (!tableEl) {
        return;
    }

    const columnsWidth = getColumnsWidth(tableEl),
        rowsHeight = getRowsHeight(tableEl),

        fixedHeadHeight = sum(rowsHeight[TableFragment.HEAD]) || 0,
        fixedFootHeight = sum(rowsHeight[TableFragment.FOOT]) || 0;

    /**
     * fix horizontal scroll style
     */
    fixTableHorizontalScroll(wrapperEl, props);

    /**
     * center
     */
    maskCenterBody(wrapperEl, tableEl, fixedHeadHeight, fixedFootHeight);
    fixTableWidth(wrapperEl, columnsWidth, null, props);
    fixTableHeight(wrapperEl, rowsHeight, null, props);

    /**
     * left
     */
    fixTableWidth(wrapperEl, columnsWidth, HorizontalAlign.LEFT, props);
    fixTableHeight(wrapperEl, rowsHeight, HorizontalAlign.LEFT, props);

    /**
     * right
     */
    fixTableWidth(wrapperEl, columnsWidth, HorizontalAlign.RIGHT, props);
    fixTableHeight(wrapperEl, rowsHeight, HorizontalAlign.RIGHT, props);

    /**
     * fix vertical scroll style
     */
    fixTableVerticalScroll(wrapperEl, props);

}

function updateHorizontalScrollClassNames(wrapperEl, scrollerEl) {

    if (!wrapperEl || !scrollerEl) {
        return;
    }

    removeClass(wrapperEl, 'scroll-left');
    removeClass(wrapperEl, 'scroll-center');
    removeClass(wrapperEl, 'scroll-right');

    const {scrollWidth, offsetWidth, scrollLeft} = scrollerEl,
        verticalScrollBarSize = ScrollBar.getSize(Direction.VERTICAL);

    // no scroll
    if (scrollWidth === offsetWidth - verticalScrollBarSize) {
        return;
    }

    if (scrollLeft === 0) {
        addClass(wrapperEl, 'scroll-left');
    } else if (scrollLeft === scrollWidth - offsetWidth + verticalScrollBarSize) {
        addClass(wrapperEl, 'scroll-right');
    } else {
        addClass(wrapperEl, 'scroll-center');
    }

}

export default {
    hasFixed,
    getColumnsWidth,
    getRowsHeight,
    getbodyScollerHeight,
    fixLayout,
    updateHorizontalScrollClassNames
};
<|endoftext|>


<commit_before>import { Trans } from "@lingui/macro";
import classNames from "classnames/dedupe";
import PropTypes from "prop-types";
import React from "react";

class FullScreenModalHeaderActions extends React.Component {
  getActions() {
    const { actions } = this.props;

    if (!actions || actions.length === 0) {
      return null;
    }

    return actions.map(
      ({ className, clickHandler, label, node, disabled }, index) => {
        if (node) {
          return node;
        }

        const classes = classNames("button flush-top", className);

        return (
          <Trans
            id={label}
            render={
              <button
                className={classes}
                disabled={disabled}
                key={index}
                onClick={clickHandler}
              />
            }
          />
        );
      }
    );
  }

  render() {
    const { className, type } = this.props;

    const classes = classNames(
      `modal-full-screen-actions modal-full-screen-actions-${type} flush-vertical`,
      className
    );

    return
{this.getActions()}
;
  }
}

const classProps = PropTypes.oneOfType([
  PropTypes.array,
  PropTypes.object,
  PropTypes.string
]);

FullScreenModalHeaderActions.propTypes = {
  actions: PropTypes.arrayOf(
    PropTypes.shape({
      className: classProps,
      clickHandler: PropTypes.func,
      label: PropTypes.node
    })
  ),
  className: classProps,
  type: PropTypes.oneOf(["primary", "secondary"]).isRequired
};

module.exports = FullScreenModalHeaderActions;
<commit_msg>fix(FullScreenModalHeaderActions): console.errors<commit_after>import { Trans } from "@lingui/macro";
import classNames from "classnames/dedupe";
import PropTypes from "prop-types";
import React from "react";

class FullScreenModalHeaderActions extends React.Component {
  getActions() {
    const { actions } = this.props;

    if (!actions || actions.length === 0) {
      return null;
    }

    return actions.map(
      ({ className, clickHandler, label, node, disabled }, index) => {
        if (node) {
          return node;
        }

        const classes = classNames("button flush-top", className);

        return (
          <button
            className={classes}
            disabled={disabled}
            key={index}
            onClick={clickHandler}
          >
            <Trans id={label} />
          </button>
        );
      }
    );
  }

  render() {
    const { className, type } = this.props;

    const classes = classNames(
      `modal-full-screen-actions modal-full-screen-actions-${type} flush-vertical`,
      className
    );

    return
{this.getActions()}
;
  }
}

const classProps = PropTypes.oneOfType([
  PropTypes.array,
  PropTypes.object,
  PropTypes.string
]);

FullScreenModalHeaderActions.propTypes = {
  actions: PropTypes.arrayOf(
    PropTypes.shape({
      className: classProps,
      clickHandler: PropTypes.func,
      label: PropTypes.node
    })
  ),
  className: classProps,
  type: PropTypes.oneOf(["primary", "secondary"]).isRequired
};

module.exports = FullScreenModalHeaderActions;
<|endoftext|>


<commit_before>import * as types from './actionTypes';
import GithubAPI from '../api/githubAPI';

export function oauthTempCodeReceived(tempCode) {
    return {type: types.OAUTH_TEMP_CODE_RECEIVED, tempCode};
}

export function oauthTokenReceived(token) {
  return {type: types.OAUTH_TOKEN_RECEIVED, token};
}

export function oauthTokenDestroyed() {
  return {type: types.OAUTH_TOKEN_DESTROYED};
}

export function oauthAuthUserLoaded(user) {
  return {type: types.OAUTH_AUTH_USER_LOADED, user};
}

export function storeOAuthTempCode(tempCode) {
  return function (dispatch){
    dispatch(oauthTempCodeReceived(tempCode));
  };
}

export function storeOAuthToken(token) {
  return function (dispatch){
    dispatch(oauthTokenReceived(token));
  };
}

export function destroyOAuthToken() {
  return function (dispatch){
    dispatch(oauthTokenDestroyed());
  };
}

export function loadAuthenticatedUser() {
  return function (dispatch){
    return GithubAPI.getAuthenticatedUser().then(user => {
      dispatch(oauthAuthUserLoaded(user));
    }).catch(error => {
      //TODO: Improve error handling instead of re-throwing error
      throw(error);
    });
  };
}
<commit_msg>Make destroyOAuthToken action destroy & recreate Octokat instance<commit_after>import * as types from './actionTypes';
import GithubAPI from '../api/githubAPI';

export function oauthTempCodeReceived(tempCode) {
    return {type: types.OAUTH_TEMP_CODE_RECEIVED, tempCode};
}

export function oauthTokenReceived(token) {
  return {type: types.OAUTH_TOKEN_RECEIVED, token};
}

export function oauthTokenDestroyed() {
  return {type: types.OAUTH_TOKEN_DESTROYED};
}

export function oauthAuthUserLoaded(user) {
  return {type: types.OAUTH_AUTH_USER_LOADED, user};
}

export function storeOAuthTempCode(tempCode) {
  return function (dispatch){
    dispatch(oauthTempCodeReceived(tempCode));
  };
}

export function storeOAuthToken(token) {
  return function (dispatch){
    dispatch(oauthTokenReceived(token));
  };
}

export function destroyOAuthToken() {
  return function (dispatch){
    return GithubAPI.removeTokenFromOcto().then(() => {
      dispatch(oauthTokenDestroyed());
    }).catch(error => {
      //TODO: Improve error handling instead of re-throwing error
      throw(error);
    });
  };
}

export function loadAuthenticatedUser() {
  return function (dispatch){
    return GithubAPI.getAuthenticatedUser().then(user => {
      dispatch(oauthAuthUserLoaded(user));
    }).catch(error => {
      //TODO: Improve error handling instead of re-throwing error
      throw(error);
    });
  };
}
<|endoftext|>


<commit_before>import base from './apiBase'

export default class extends base {

    async getAction(){
        let tags;
        let postModel = this.model('post');
        if (this.id) {
            tags = await this.modelInstance.where({id: this.id}).find();
            tags.count = await postModel.where({tag_ids: ['like', '%'+this.id+'%']}).count('*');
        } else {
            tags = await this.modelInstance.order('id').select();
            let counts = await postModel.order('id').select();

            tags.forEach(tag => {
                let countIndex = 0;
                counts.forEach( count => {
                    if(count.tag_ids.indexOf(tag.id) > -1) {
                        countIndex++;
                    }
                });
                tag.count = countIndex;
            });
        }

        return this.success(tags);
    }

    async postAction(){
        let data = this.post();
        let userId = this.userInfo.id;

        if(think.isEmpty(data)){
            return this.fail('数据为空');
        }
        delete data.id;

        let result = await this.modelInstance.where({name: data.name}).count();
        if (result) {
            return this.fail('标签名已经存在');
        }
        data.user_id = userId;
        let insertId = await this.modelInstance.add(data);
        return this.success({id: insertId});
    }

}<commit_msg>add fk_post_tag relation table<commit_after>import base from './apiBase'

export default class extends base {

    async getAction(){
        let tags, counts;
        let postTagModel = this.model('post_tag');
        if (this.id) {
            tags = await this.modelInstance.where({id: this.id}).find();
            tags.count = await postTagModel.where({tag_id: this.id}).count('*');
        } else {
            tags = await this.modelInstance.order('id').select();
            counts = await postTagModel.order('id').select();

            tags.forEach(tag => {
                let countIndex = 0;
                counts.forEach( count => {
                    if(count.tag_id == tag.id) {
                        countIndex++;
                    }
                });
                tag.count = countIndex;
            });
        }

        return this.success(tags);
    }

    async postAction(){
        let data = this.post();
        let userId = this.userInfo.id;

        if(think.isEmpty(data)){
            return this.fail('数据为空');
        }
        delete data.id;

        let result = await this.modelInstance.where({name: data.name}).count();
        if (result) {
            return this.fail('标签名已经存在');
        }
        data.user_id = userId;
        let insertId = await this.modelInstance.add(data);
        return this.success({id: insertId});
    }

}<|endoftext|>