<commit_before>'use strict';

LocusZoom.KnownDataSources.extend("AssociationLZ", "AssociationPheWeb", {
    getURL: function (state, chain, fields) {
        return this.url + "results/?filter=chromosome in  '" + state.chr + "'" +
            " and position ge " + state.start +
            " and position le " + state.end;
    },
    // Although the layout fields array is useful for specifying transforms, this source will magically re-add
    //  any data that was not explicitly requested
    extractFields: function(data, fields, outnames, trans) {
        // The field "all" has a special meaning, and only exists to trigger a request to this source.
        // We're not actually trying to request a field by that name.
        var has_all = fields.indexOf("all");
        if (has_all !== -1) {
            fields.splice(has_all, 1);
            outnames.splice(has_all, 1);
            trans.splice(has_all, 1);
        }
        // Find all fields that have not been requested (sans transforms), and add them back to the fields array
        if (data.length) {
            var fieldnames = Object.keys(data[0]);
            var ns = this.source_id + ":"; // ensure that namespacing is applied to the fields
            fieldnames.forEach(function(item) {
                var ref = fields.indexOf(item);
                if (ref === -1 || trans[ref]) {
                    fields.push(item);
                    outnames.push(ns + item);
                    trans.push(null);
                }
            });
        }
        return LocusZoom.Data.AssociationSource.prototype.extractFields.call(this, data, fields, outnames, trans);
    }
});

// Modified LD source that handles `NaN` in response.
LocusZoom.KnownDataSources.extend("LDLZ", "LDPheweb", {
    parseResponse: function (resp, chain, fields, outnames, trans) {
        var json;
        try {
            json = JSON.parse(resp);
        } catch (exc) {
            if (exc instanceof SyntaxError) {
                resp = resp.replace(/\bNaN\b/g, 'null');
                var junk = JSON.parse(resp);
                json = { data:{} };
                Object.keys(junk).forEach(function(key) {
                    if (key !== 'data') { json[key] = junk[key]; }
                });
                var json_data_keys = Object.keys(junk.data);
                json_data_keys.forEach(function(key) {
                    json.data[key] = [];
                });
                for (var i=0; i<junk.data.rsquare.length; i++) {
                    if (junk.data.rsquare[i] !== null) {
                        json_data_keys.forEach(function(key) {
                            json.data[key].push(junk.data[key][i]);
                        });
                    }
                }
            } else {
                console.error('Unhandled exception when processing LD source', exc);
                throw exc;
            }
        }
        return LocusZoom.Data.LDSource.prototype.parseResponse.call(this, json, chain, fields, outnames);
    }
});

LocusZoom.TransformationFunctions.set("percent", function(x) {
    if (x === 1) { return "100%"; }
    x = (x * 100).toPrecision(2);
    if (x.indexOf('.') !== -1) { x = x.replace(/0+$/, ''); }
    if (x.endsWith('.')) { x = x.substr(0, x.length-1); }
    return x + '%';
});

(function() {
    // Define LocusZoom Data Sources object
    var localBase = window.model.urlprefix + "/api/region/" + window.pheno.phenocode + "/lz-";
    var remoteBase = "https://portaldev.sph.umich.edu/api/v1/";
    var data_sources = new LocusZoom.DataSources()
        .add("assoc", ["AssociationPheWeb", localBase])
        .add("catalog", ["GwasCatalogLZ", {url: remoteBase + 'annotation/gwascatalog/results/', params: { source: 2 }}])
        .add("ld", ["LDPheweb", {url: remoteBase + "pair/LD/", params: { pvalue_field: "assoc:pvalue|neglog10_or_100" }}])
        .add("gene", ["GeneLZ", { url: remoteBase + "annotation/genes/", params: {source: 2} }])
        .add("recomb", ["RecombLZ", { url: remoteBase + "annotation/recomb/results/", params: {source: 15} }]);

    LocusZoom.TransformationFunctions.set("neglog10_or_100", function(x) {
        if (x === 0) return 100;
        return -Math.log(x) / Math.LN10;
    });

    // dashboard components
    LocusZoom.Dashboard.Components.add("region", function(layout){
        LocusZoom.Dashboard.Component.apply(this, arguments);
        this.update = function(){
            if (!isNaN(this.parent_plot.state.chr) && !isNaN(this.parent_plot.state.start) && !isNaN(this.parent_plot.state.end)
                && this.parent_plot.state.chr != null && this.parent_plot.state.start != null && this.parent_plot.state.end != null){
                this.selector.style("display", null);
                this.selector.text(
                    'chr' + this.parent_plot.state.chr.toString() + ': ' +
                    LocusZoom.positionIntToString(this.parent_plot.state.start, 6, true).replace(' ','') + ' - ' +
                    LocusZoom.positionIntToString(this.parent_plot.state.end, 6, true).replace(' ', ''));
            } else {
                this.selector.style("display", "none");
            }
            if (layout.class){ this.selector.attr("class", layout.class); }
            if (layout.style){ this.selector.style(layout.style); }
            return this;
        };
    });

    LocusZoom.Dashboard.Components.add("ldrefsource_selector", function(layout){
        LocusZoom.Dashboard.Component.apply(this, arguments);
        this.initialize = function(){
            this.parent_plot.state.ldrefsource = 1;
        };

        this.update = function() {
            if (this.button){ return this; }

            this.button = new LocusZoom.Dashboard.Component.Button(this);
            this.button
                .setHtml("LD source: 1000G ALL")
                .setTitle("click to switch to next LD source")
                .setOnclick(function(){
                    if (this.parent_plot.state.ldrefsource === 1) {
                        this.parent_plot.state.ldrefsource = 2;
                        this.button.setHtml('LD source: 1000G EUR');
                    } else {
                        this.parent_plot.state.ldrefsource = 1;
                        this.button.setHtml('LD source: 1000G ALL');
                    }
                    this.button.show();
                    this.parent_plot.applyState({});
                }.bind(this));
            this.button.show();
            return this;
        };
    });

    function add_dashboard_button(name, func) {
        LocusZoom.Dashboard.Components.add(name, function(layout){
            LocusZoom.Dashboard.Component.apply(this, arguments);
            this.update = function(){
                if (this.button)
                    return this;
                this.button = new LocusZoom.Dashboard.Component.Button(this)
                    .setColor(layout.color).setHtml(layout.text).setTitle(layout.title)
                    .setOnclick(func(layout).bind(this));
                this.button.show();
                return this.update();
            };
        });
    }

    add_dashboard_button('link', function(layout) {
        return function() {
            window.location.href = layout.url;
        };
    });
    add_dashboard_button('move', function(layout) {
        // see also the default component `shift_region`
        return function() {
            var start = this.parent_plot.state.start;
            var end = this.parent_plot.state.end;
            var shift = Math.floor(end - start) * layout.direction;
            this.parent_plot.applyState({
                chr: this.parent_plot.state.chr,
                start: start + shift,
                end: end + shift
            });
        }
    });

    // Define the layout, then fetch it via the LZ machinery responsible for namespacing
    var layout = LocusZoom.Layouts.get("plot", "association_catalog", {
        unnamespaced: true,
        width: 800,
        // height: 550,
        responsive_resize: true, // TODO: perhaps setting this to `false` would fix all my sizing problems.
        max_region_scale: 5e5,
        dashboard: {
            components: [{
                type: 'link',
                title: 'Go to Manhattan Plot',
                text:' Manhattan Plot',
                url: window.model.urlprefix + '/pheno/' + window.pheno.phenocode
            },{
                type: 'move',
                text: '<<',
                title: 'Shift view 1/4 to the left',
                direction: -0.75,
                group_position: "start"
            },{
                type: 'move',
                text: '<',
                title: 'Shift view 1/4 to the left',
                direction: -0.25,
                group_position: "middle"
            },{
                type: 'zoom_region',
                button_html: 'z+',
                title: 'zoom in 2x',
                step: -0.5,
                group_position: "middle"
            },{
                type: 'zoom_region',
                button_html: 'z-',
                title: 'zoom out 2x',
                step: 1,
                group_position: "middle"
            },{
                type: 'move',
                text: '>',
                title: 'Shift view 1/4 to the right',
                direction: 0.25,
                group_position: "middle"
            },{
                type: 'move',
                text: '>>',
                title: 'Shift view 3/4 to the right',
                direction: 0.75,
                group_position: "end"
            },{
                "type": "download",
                "position": "right"
            },{
                "type": "ldrefsource_selector",
                "position": "right"
            }]
    },
        panels: [
            function() {
                var l = LocusZoom.Layouts.get("panel", "annotation_catalog", {
                    unnamespaced: true,
                    height: 40, min_height: 40,
                    margin: { top: 30, bottom: 10 },
                });
                l.data_layers[0].fields = [  // Tell annotation track the field names as used by PheWeb
                    "{{namespace[assoc]}}chr", "{{namespace[assoc]}}position",
                    "{{namespace[catalog]}}variant", "{{namespace[catalog]}}rsid", "{{namespace[catalog]}}trait", "{{namespace[catalog]}}log_pvalue"
                ];
                return l;
            }(),
            LocusZoom.Layouts.get("panel", "association_catalog", {
                unnamespaced: true,
                // proportional_height: 0.5,
                height: 200, // I don't understand how this parameter works.
                // min_height: 250,
                margin: { top: 10 },
                dashboard: {
                    components: [
                        {
                            type: "toggle_legend",
                            position: "right",
                            color: "green"
                        },
                        {
                            type: "display_options",
                            position: "right",
                            color: "blue",
                            // Below: special config specific to this widget
                            button_html: "Display options...",
                            button_title: "Control how plot items are displayed",

                            layer_name: "associationpvaluescatalog",
                            default_config_display_name: "No catalog labels (default)", // display name for the default plot color option (allow user to revert to plot defaults)

                            options: [
                                {
                                    // First dropdown menu item
                                    display_name: "Label catalog traits",  // Human readable representation of field name
                                    display: {  // Specify layout directives that control display of the plot for this option
                                        label: {
                                            text: "{{{{namespace[catalog]}}trait}}",
                                            spacing: 6,
                                            lines: {
                                                style: {
                                                    "stroke-width": "2px",
                                                    "stroke": "#333333",
                                                    "stroke-dasharray": "2px 2px"
                                                }
                                            },
                                            filters: [
                                                // Only label points if they are significant for some trait in the catalog, AND in high LD
                                                //  with the top hit of interest
                                                {
                                                    field: "{{namespace[catalog]}}trait",
                                                    operator: "!=",
                                                    value: null
                                                },
                                                {
                                                    field: "{{namespace[catalog]}}log_pvalue",
                                                    operator: ">",
                                                    value: 7.301
                                                },
                                                {
                                                    field: "{{namespace[ld]}}state",
                                                    operator: ">",
                                                    value: 0.4
                                                },
                                            ],
                                            style: {
                                                "font-size": "10px",
                                                "font-weight": "bold",
                                                "fill": "#333333"
                                            }
                                        }
                                    }
                                }
                            ]
                        }
                    ]
                },
                data_layers: [
                    LocusZoom.Layouts.get("data_layer", "significance", { unnamespaced: true }),
                    LocusZoom.Layouts.get("data_layer", "recomb_rate", { unnamespaced: true }),
                    LocusZoom.Layouts.get("data_layer", "association_pvalues_catalog", {
                        unnamespaced: true,
                        fields: [
                            "{{namespace[assoc]}}all", // special mock value for the custom source
                            "{{namespace[assoc]}}id",
                            "{{namespace[assoc]}}position",
                            "{{namespace[assoc]}}pvalue|neglog10_or_100",
                            "{{namespace[ld]}}state", "{{namespace[ld]}}isrefvar",
                            "{{namespace[catalog]}}rsid", "{{namespace[catalog]}}trait", "{{namespace[catalog]}}log_pvalue"
                        ],
                        id_field: "{{namespace[assoc]}}id",
                        tooltip: {
                            closable: true,
                            show: {
                                "or": ["highlighted", "selected"]
                            },
                            hide: {
                                "and": ["unhighlighted", "unselected"]
                            },
                            html: "{{{{namespace[assoc]}}id}}

" +
                                "Go to PheWAS" +
                                "{{#if {{namespace[catalog]}}rsid}}
See hits in GWAS catalog{{/if}}" +
                                "
Make LD Reference
" +
                                window.model.tooltip_lztemplate
                        },
                        x_axis: { field: "{{namespace[assoc]}}position" },
                        y_axis: {
                            axis: 1,
                            field: "{{namespace[assoc]}}pvalue|neglog10_or_100",
                            floor: 0,
                            upper_buffer: 0.1,
                            min_extent: [0, 10]
                        }
                    })
                ],
            }),
            LocusZoom.Layouts.get("panel", "genes", {
                unnamespaced: true,
                // proportional_height: 0.5,
                dashboard: {
                    components: [{
                        type: "resize_to_data",
                        position: "right",
                        color: "blue"
                    }]
                },
                data_layers: [
                    LocusZoom.Layouts.get("data_layer", "genes", {
                        unnamespaced: true,
                        fields: ["{{namespace[gene]}}all"],
                        tooltip: {
                            closable: true,
                            show: {
                                or: ["highlighted", "selected"]
                            },
                            hide: {
                                and: ["unhighlighted", "unselected"]
                            },
                            html: "
{{gene_name}}

Gene ID: {{gene_id}}

Transcript ID: {{transcript_id}}

More data on ExAC

"
                        },
                        label_exon_spacing: 3,
                        exon_height: 8,
                        bounding_box_padding: 5,
                        track_vertical_spacing: 5
                    })
                ],
            })
        ]
    });
    LocusZoom.Layouts.add("plot", "pheweb_association", layout);
    layout = LocusZoom.Layouts.get("plot", "pheweb_association");

    $(function() {
        // Populate the div with a LocusZoom plot using the default layout
        window.plot = LocusZoom.populate("#lz-1", data_sources, layout);
    });
})();
<commit_msg>Adjust LZ GWASCat annotation track height and hit areas<commit_after>'use strict';

LocusZoom.KnownDataSources.extend("AssociationLZ", "AssociationPheWeb", {
    getURL: function (state, chain, fields) {
        return this.url + "results/?filter=chromosome in  '" + state.chr + "'" +
            " and position ge " + state.start +
            " and position le " + state.end;
    },
    // Although the layout fields array is useful for specifying transforms, this source will magically re-add
    //  any data that was not explicitly requested
    extractFields: function(data, fields, outnames, trans) {
        // The field "all" has a special meaning, and only exists to trigger a request to this source.
        // We're not actually trying to request a field by that name.
        var has_all = fields.indexOf("all");
        if (has_all !== -1) {
            fields.splice(has_all, 1);
            outnames.splice(has_all, 1);
            trans.splice(has_all, 1);
        }
        // Find all fields that have not been requested (sans transforms), and add them back to the fields array
        if (data.length) {
            var fieldnames = Object.keys(data[0]);
            var ns = this.source_id + ":"; // ensure that namespacing is applied to the fields
            fieldnames.forEach(function(item) {
                var ref = fields.indexOf(item);
                if (ref === -1 || trans[ref]) {
                    fields.push(item);
                    outnames.push(ns + item);
                    trans.push(null);
                }
            });
        }
        return LocusZoom.Data.AssociationSource.prototype.extractFields.call(this, data, fields, outnames, trans);
    }
});

// Modified LD source that handles `NaN` in response.
LocusZoom.KnownDataSources.extend("LDLZ", "LDPheweb", {
    parseResponse: function (resp, chain, fields, outnames, trans) {
        var json;
        try {
            json = JSON.parse(resp);
        } catch (exc) {
            if (exc instanceof SyntaxError) {
                resp = resp.replace(/\bNaN\b/g, 'null');
                var junk = JSON.parse(resp);
                json = { data:{} };
                Object.keys(junk).forEach(function(key) {
                    if (key !== 'data') { json[key] = junk[key]; }
                });
                var json_data_keys = Object.keys(junk.data);
                json_data_keys.forEach(function(key) {
                    json.data[key] = [];
                });
                for (var i=0; i<junk.data.rsquare.length; i++) {
                    if (junk.data.rsquare[i] !== null) {
                        json_data_keys.forEach(function(key) {
                            json.data[key].push(junk.data[key][i]);
                        });
                    }
                }
            } else {
                console.error('Unhandled exception when processing LD source', exc);
                throw exc;
            }
        }
        return LocusZoom.Data.LDSource.prototype.parseResponse.call(this, json, chain, fields, outnames);
    }
});

LocusZoom.TransformationFunctions.set("percent", function(x) {
    if (x === 1) { return "100%"; }
    x = (x * 100).toPrecision(2);
    if (x.indexOf('.') !== -1) { x = x.replace(/0+$/, ''); }
    if (x.endsWith('.')) { x = x.substr(0, x.length-1); }
    return x + '%';
});

(function() {
    // Define LocusZoom Data Sources object
    var localBase = window.model.urlprefix + "/api/region/" + window.pheno.phenocode + "/lz-";
    var remoteBase = "https://portaldev.sph.umich.edu/api/v1/";
    var data_sources = new LocusZoom.DataSources()
        .add("assoc", ["AssociationPheWeb", localBase])
        .add("catalog", ["GwasCatalogLZ", {url: remoteBase + 'annotation/gwascatalog/results/', params: { source: 2 }}])
        .add("ld", ["LDPheweb", {url: remoteBase + "pair/LD/", params: { pvalue_field: "assoc:pvalue|neglog10_or_100" }}])
        .add("gene", ["GeneLZ", { url: remoteBase + "annotation/genes/", params: {source: 2} }])
        .add("recomb", ["RecombLZ", { url: remoteBase + "annotation/recomb/results/", params: {source: 15} }]);

    LocusZoom.TransformationFunctions.set("neglog10_or_100", function(x) {
        if (x === 0) return 100;
        return -Math.log(x) / Math.LN10;
    });

    // dashboard components
    LocusZoom.Dashboard.Components.add("region", function(layout){
        LocusZoom.Dashboard.Component.apply(this, arguments);
        this.update = function(){
            if (!isNaN(this.parent_plot.state.chr) && !isNaN(this.parent_plot.state.start) && !isNaN(this.parent_plot.state.end)
                && this.parent_plot.state.chr != null && this.parent_plot.state.start != null && this.parent_plot.state.end != null){
                this.selector.style("display", null);
                this.selector.text(
                    'chr' + this.parent_plot.state.chr.toString() + ': ' +
                    LocusZoom.positionIntToString(this.parent_plot.state.start, 6, true).replace(' ','') + ' - ' +
                    LocusZoom.positionIntToString(this.parent_plot.state.end, 6, true).replace(' ', ''));
            } else {
                this.selector.style("display", "none");
            }
            if (layout.class){ this.selector.attr("class", layout.class); }
            if (layout.style){ this.selector.style(layout.style); }
            return this;
        };
    });

    LocusZoom.Dashboard.Components.add("ldrefsource_selector", function(layout){
        LocusZoom.Dashboard.Component.apply(this, arguments);
        this.initialize = function(){
            this.parent_plot.state.ldrefsource = 1;
        };

        this.update = function() {
            if (this.button){ return this; }

            this.button = new LocusZoom.Dashboard.Component.Button(this);
            this.button
                .setHtml("LD source: 1000G ALL")
                .setTitle("click to switch to next LD source")
                .setOnclick(function(){
                    if (this.parent_plot.state.ldrefsource === 1) {
                        this.parent_plot.state.ldrefsource = 2;
                        this.button.setHtml('LD source: 1000G EUR');
                    } else {
                        this.parent_plot.state.ldrefsource = 1;
                        this.button.setHtml('LD source: 1000G ALL');
                    }
                    this.button.show();
                    this.parent_plot.applyState({});
                }.bind(this));
            this.button.show();
            return this;
        };
    });

    function add_dashboard_button(name, func) {
        LocusZoom.Dashboard.Components.add(name, function(layout){
            LocusZoom.Dashboard.Component.apply(this, arguments);
            this.update = function(){
                if (this.button)
                    return this;
                this.button = new LocusZoom.Dashboard.Component.Button(this)
                    .setColor(layout.color).setHtml(layout.text).setTitle(layout.title)
                    .setOnclick(func(layout).bind(this));
                this.button.show();
                return this.update();
            };
        });
    }

    add_dashboard_button('link', function(layout) {
        return function() {
            window.location.href = layout.url;
        };
    });
    add_dashboard_button('move', function(layout) {
        // see also the default component `shift_region`
        return function() {
            var start = this.parent_plot.state.start;
            var end = this.parent_plot.state.end;
            var shift = Math.floor(end - start) * layout.direction;
            this.parent_plot.applyState({
                chr: this.parent_plot.state.chr,
                start: start + shift,
                end: end + shift
            });
        }
    });

    // Define the layout, then fetch it via the LZ machinery responsible for namespacing
    var layout = LocusZoom.Layouts.get("plot", "association_catalog", {
        unnamespaced: true,
        width: 800,
        // height: 550,
        responsive_resize: true, // TODO: perhaps setting this to `false` would fix all my sizing problems.
        max_region_scale: 5e5,
        dashboard: {
            components: [{
                type: 'link',
                title: 'Go to Manhattan Plot',
                text:' Manhattan Plot',
                url: window.model.urlprefix + '/pheno/' + window.pheno.phenocode
            },{
                type: 'move',
                text: '<<',
                title: 'Shift view 1/4 to the left',
                direction: -0.75,
                group_position: "start"
            },{
                type: 'move',
                text: '<',
                title: 'Shift view 1/4 to the left',
                direction: -0.25,
                group_position: "middle"
            },{
                type: 'zoom_region',
                button_html: 'z+',
                title: 'zoom in 2x',
                step: -0.5,
                group_position: "middle"
            },{
                type: 'zoom_region',
                button_html: 'z-',
                title: 'zoom out 2x',
                step: 1,
                group_position: "middle"
            },{
                type: 'move',
                text: '>',
                title: 'Shift view 1/4 to the right',
                direction: 0.25,
                group_position: "middle"
            },{
                type: 'move',
                text: '>>',
                title: 'Shift view 3/4 to the right',
                direction: 0.75,
                group_position: "end"
            },{
                "type": "download",
                "position": "right"
            },{
                "type": "ldrefsource_selector",
                "position": "right"
            }]
    },
        panels: [
            function() {
                var l = LocusZoom.Layouts.get("panel", "annotation_catalog", {
                    unnamespaced: true,
                    height: 52, min_height: 52,
                    margin: { top: 30, bottom: 13 },
                });
                l.data_layers[0].fields = [  // Tell annotation track the field names as used by PheWeb
                    "{{namespace[assoc]}}chr", "{{namespace[assoc]}}position",
                    "{{namespace[catalog]}}variant", "{{namespace[catalog]}}rsid", "{{namespace[catalog]}}trait", "{{namespace[catalog]}}log_pvalue"
                ];
                l.data_layers[0].hit_area_width = 50;
                return l;
            }(),
            LocusZoom.Layouts.get("panel", "association_catalog", {
                unnamespaced: true,
                height: 200, min_height: 200,
                margin: { top: 10 },
                dashboard: {
                    components: [
                        {
                            type: "toggle_legend",
                            position: "right",
                            color: "green"
                        },
                        {
                            type: "display_options",
                            position: "right",
                            color: "blue",
                            // Below: special config specific to this widget
                            button_html: "Display options...",
                            button_title: "Control how plot items are displayed",

                            layer_name: "associationpvaluescatalog",
                            default_config_display_name: "No catalog labels (default)", // display name for the default plot color option (allow user to revert to plot defaults)

                            options: [
                                {
                                    // First dropdown menu item
                                    display_name: "Label catalog traits",  // Human readable representation of field name
                                    display: {  // Specify layout directives that control display of the plot for this option
                                        label: {
                                            text: "{{{{namespace[catalog]}}trait}}",
                                            spacing: 6,
                                            lines: {
                                                style: {
                                                    "stroke-width": "2px",
                                                    "stroke": "#333333",
                                                    "stroke-dasharray": "2px 2px"
                                                }
                                            },
                                            filters: [
                                                // Only label points if they are significant for some trait in the catalog, AND in high LD
                                                //  with the top hit of interest
                                                {
                                                    field: "{{namespace[catalog]}}trait",
                                                    operator: "!=",
                                                    value: null
                                                },
                                                {
                                                    field: "{{namespace[catalog]}}log_pvalue",
                                                    operator: ">",
                                                    value: 7.301
                                                },
                                                {
                                                    field: "{{namespace[ld]}}state",
                                                    operator: ">",
                                                    value: 0.4
                                                },
                                            ],
                                            style: {
                                                "font-size": "10px",
                                                "font-weight": "bold",
                                                "fill": "#333333"
                                            }
                                        }
                                    }
                                }
                            ]
                        }
                    ]
                },
                data_layers: [
                    LocusZoom.Layouts.get("data_layer", "significance", { unnamespaced: true }),
                    LocusZoom.Layouts.get("data_layer", "recomb_rate", { unnamespaced: true }),
                    LocusZoom.Layouts.get("data_layer", "association_pvalues_catalog", {
                        unnamespaced: true,
                        fields: [
                            "{{namespace[assoc]}}all", // special mock value for the custom source
                            "{{namespace[assoc]}}id",
                            "{{namespace[assoc]}}position",
                            "{{namespace[assoc]}}pvalue|neglog10_or_100",
                            "{{namespace[ld]}}state", "{{namespace[ld]}}isrefvar",
                            "{{namespace[catalog]}}rsid", "{{namespace[catalog]}}trait", "{{namespace[catalog]}}log_pvalue"
                        ],
                        id_field: "{{namespace[assoc]}}id",
                        tooltip: {
                            closable: true,
                            show: {
                                "or": ["highlighted", "selected"]
                            },
                            hide: {
                                "and": ["unhighlighted", "unselected"]
                            },
                            html: "{{{{namespace[assoc]}}id}}

" +
                                "Go to PheWAS" +
                                "{{#if {{namespace[catalog]}}rsid}}
See hits in GWAS catalog{{/if}}" +
                                "
Make LD Reference
" +
                                window.model.tooltip_lztemplate
                        },
                        x_axis: { field: "{{namespace[assoc]}}position" },
                        y_axis: {
                            axis: 1,
                            field: "{{namespace[assoc]}}pvalue|neglog10_or_100",
                            floor: 0,
                            upper_buffer: 0.1,
                            min_extent: [0, 10]
                        }
                    })
                ],
            }),
            LocusZoom.Layouts.get("panel", "genes", {
                unnamespaced: true,
                // proportional_height: 0.5,
                dashboard: {
                    components: [{
                        type: "resize_to_data",
                        position: "right",
                        color: "blue"
                    }]
                },
                data_layers: [
                    LocusZoom.Layouts.get("data_layer", "genes", {
                        unnamespaced: true,
                        fields: ["{{namespace[gene]}}all"],
                        tooltip: {
                            closable: true,
                            show: {
                                or: ["highlighted", "selected"]
                            },
                            hide: {
                                and: ["unhighlighted", "unselected"]
                            },
                            html: "
{{gene_name}}

Gene ID: {{gene_id}}

Transcript ID: {{transcript_id}}

More data on ExAC

"
                        },
                        label_exon_spacing: 3,
                        exon_height: 8,
                        bounding_box_padding: 5,
                        track_vertical_spacing: 5
                    })
                ],
            })
        ]
    });
    LocusZoom.Layouts.add("plot", "pheweb_association", layout);
    layout = LocusZoom.Layouts.get("plot", "pheweb_association");

    $(function() {
        // Populate the div with a LocusZoom plot using the default layout
        window.plot = LocusZoom.populate("#lz-1", data_sources, layout);
    });
})();
<|endoftext|>


<commit_before>var sys = require('pex-sys');
var glu = require('pex-glu');
var geom = require('pex-geom');
var gen = require('pex-gen');
var materials = require('pex-materials');
var color = require('pex-color');
var gui = require('pex-gui');

var Cube = gen.Cube;
var Sphere = gen.Sphere;
var Mesh = glu.Mesh;
var TexturedCubeMap = materials.TexturedCubeMap;
var SkyBox = materials.SkyBox;
var PerspectiveCamera = glu.PerspectiveCamera;
var Arcball = glu.Arcball;
var Color = color.Color;
var TextureCube = glu.TextureCube;
var GUI = gui.GUI;

sys.Window.create({
  settings: {
    width: 1280,
    height: 720,
    type: '3d',
    fullscreen: sys.Platform.isBrowser
  },
  lod: 0,
  init: function() {
    this.gui = new GUI(this);
    this.gui.addParam('LOD', this, 'lod', { min: 0, max: 8 });

    var levels = ['m00', 'm01', 'm02', 'm03', 'm04', 'm05', 'm06', 'm07', 'm08'];
    var sides = ['c00', 'c01', 'c02', 'c03', 'c04', 'c05'];

    var cubeMapFiles = [];
    levels.forEach(function(level) {
      sides.forEach(function(side) {
        cubeMapFiles.push('../../assets/cubemaps/uffizi_lod/uffizi_' + level + '_' + side + '.png');
      });
    });

    var cubeMap = TextureCube.load(cubeMapFiles, { mipmap : true });
    this.mesh = new Mesh(new Sphere(), new TexturedCubeMap({ texture: cubeMap }));
    this.cubeMesh = new Mesh(new Cube(50), new SkyBox({ texture: cubeMap }));

    this.camera = new PerspectiveCamera(60, this.width / this.height);
    this.arcball = new Arcball(this, this.camera);
  },
  draw: function() {
    glu.clearColorAndDepth(Color.Black);
    glu.enableDepthReadAndWrite(true);
    this.cubeMesh.draw(this.camera);
    this.mesh.draw(this.camera);
    this.mesh.material.uniforms.lod = this.lod;

    this.gui.draw();
  }
});<commit_msg>Removed mipmapping when using LOD<commit_after>var sys = require('pex-sys');
var glu = require('pex-glu');
var geom = require('pex-geom');
var gen = require('pex-gen');
var materials = require('pex-materials');
var color = require('pex-color');
var gui = require('pex-gui');

var Cube = gen.Cube;
var Sphere = gen.Sphere;
var Mesh = glu.Mesh;
var TexturedCubeMap = materials.TexturedCubeMap;
var SkyBox = materials.SkyBox;
var PerspectiveCamera = glu.PerspectiveCamera;
var Arcball = glu.Arcball;
var Color = color.Color;
var TextureCube = glu.TextureCube;
var GUI = gui.GUI;

sys.Window.create({
  settings: {
    width: 1280,
    height: 720,
    type: '3d',
    fullscreen: sys.Platform.isBrowser
  },
  lod: 4,
  init: function() {
    this.gui = new GUI(this);
    this.gui.addParam('LOD', this, 'lod', { min: 0, max: 8 });

    var levels = ['m00', 'm01', 'm02', 'm03', 'm04', 'm05', 'm06', 'm07', 'm08'];
    var sides = ['c00', 'c01', 'c02', 'c03', 'c04', 'c05'];

    var cubeMapFiles = [];
    levels.forEach(function(level) {
      sides.forEach(function(side) {
        cubeMapFiles.push('../../assets/cubemaps/uffizi_lod/uffizi_' + level + '_' + side + '.png');
      });
    });

    var cubeMap = TextureCube.load(cubeMapFiles);
    this.mesh = new Mesh(new Sphere(), new TexturedCubeMap({ texture: cubeMap }));
    this.cubeMesh = new Mesh(new Cube(50), new SkyBox({ texture: cubeMap }));

    this.camera = new PerspectiveCamera(60, this.width / this.height);
    this.arcball = new Arcball(this, this.camera);
  },
  draw: function() {
    glu.clearColorAndDepth(Color.Black);
    glu.enableDepthReadAndWrite(true);
    this.cubeMesh.draw(this.camera);
    this.mesh.draw(this.camera);
    this.mesh.material.uniforms.lod = this.lod;

    this.gui.draw();
  }
});<|endoftext|>


<commit_before>var TIME = 90
  , EventEmitter = require('events').EventEmitter
  , _ = require('underscore')
  , Player = require('./player')
  , genPack = require('./genPack')
  , DELETE_DELAY = 1000 * 60 * 60
  ;

try {
  var cube = require('../cards/cube')
} catch (err) {
  console.log('no cubes found');
}

function Q(sets, type, size) {
  if (type !== 'sealed') sets = sets.slice(0, 3);
  if (type === 'cube') {
    var shuffled = _.shuffle(cube);
    sets = [shuffled, shuffled, shuffled];
  }
  this.size = Number(size);
  this.sets = sets;
  this.type = type;
  this.id = _.random(1e8);
  this.players = [];
  this.openPacks = 0;
  this.clockwise = false;
  this.started = false;
  this.ended = false;

  _.bindAll(this, 'decrement', 'meta', 'playerClose', 'end2');
}

_.extend(Q.prototype, EventEmitter.prototype);

Q.prototype.add = function(ws, pid, name) {
  var players = this.players
    , len = players.length
    , self = this
    , player
    ;

  pid = Number(pid);

  if (player = _.find(players, function(player)
    { return player.id === pid; })) {
    player.reset(ws, players.indexOf(player));
    this.meta();
    return;
  }
  if (len === this.size)
    return ws.error('q full');

  player = new Player(ws, pid, name, len, TIME);
  player.on('close', this.playerClose);
  player.on('meta', this.meta);
  player.on('pass', function(pack) {
    self.pass(pack, this);
  });

  len = players.push(player);
  if (len === this.size)
    this.start();
  else
    this.meta();
};

Q.prototype.playerClose = function(player) {
  var players = this.players;
  if (!this.started) {
    players.splice(players.indexOf(player), 1);
    _.each(players, function(player, index) {
      player.index = index;
    });
  }
  this.meta();
};

Q.prototype.pass = function(pack, player) {
  var players = this.players
    , len = players.length
    , delta = this.clockwise ? +1 : -1
    , idx = player.index + delta
    ;

  idx = (idx + len) % len; // negative modulo
  if (pack.cards.length)
    players[idx].receive(pack);
  else {
    if (!--this.openPacks)
      this.openPack();
  }
  this.meta();
};

Q.prototype.start = function() {
  this.started = true;
  if (this.type === 'sealed') {
    var sets = this.sets;
    this.ended = true;
    _.each(this.players, function(player) {
      var picks = _.chain(sets)
        .map(genPack)
        .pluck('cards')
        .flatten()
        .value()
      ;
      player.picks = picks;
      player.send('picks', picks);
    });
    this.meta();
  } else {
    this.intervalID = setInterval(this.decrement, 1000);
    this.openPack();
  }
};

Q.prototype.openPack = function() {
  var set = this.sets.shift();
  if (!set)
    return this.end();

  this.openPacks = this.size;
  this.clockwise = !this.clockwise;
  _.each(this.players, function(player) {
    player.receive(genPack(set));
  });
  this.meta();
};

Q.prototype.decrement = function() {
  _.each(this.players, function(player) {
    if (player.packs.length && !--player.time)
      player.autoPick();
  });
};

Q.prototype.end = function() {
  clearInterval(this.intervalID);
  this.ended = true;
  this.meta();
  setTimeout(this.end2, DELETE_DELAY);
};

Q.prototype.end2 = function() {
  _.invoke(this.players, 'end');
  this.emit('end'); // remove this q from router
};

Q.prototype.meta = function() {
  var players = this.players
    , arr = []
    , ended = this.ended
    , size = this.size
    ;
  _.each(players, function(player) {
    arr.push({
      dropped: player.dropped,
      hash: player.hash,
      name: player.name,
      packs: player.packs.length,
      time: player.time
    });
  });
  _.each(players, function(player, index) {
    var meta = {
      players: arr,
      ended: ended,
      index: index,
      size: size
    };
    player.send('meta', meta);
  });
};

module.exports = Q;
<commit_msg>semicolon<commit_after>var TIME = 90
  , EventEmitter = require('events').EventEmitter
  , _ = require('underscore')
  , Player = require('./player')
  , genPack = require('./genPack')
  , DELETE_DELAY = 1000 * 60 * 60
  ;

try {
  var cube = require('../cards/cube');
} catch (err) {
  console.log('no cubes found');
}

function Q(sets, type, size) {
  if (type !== 'sealed') sets = sets.slice(0, 3);
  if (type === 'cube') {
    var shuffled = _.shuffle(cube);
    sets = [shuffled, shuffled, shuffled];
  }
  this.size = Number(size);
  this.sets = sets;
  this.type = type;
  this.id = _.random(1e8);
  this.players = [];
  this.openPacks = 0;
  this.clockwise = false;
  this.started = false;
  this.ended = false;

  _.bindAll(this, 'decrement', 'meta', 'playerClose', 'end2');
}

_.extend(Q.prototype, EventEmitter.prototype);

Q.prototype.add = function(ws, pid, name) {
  var players = this.players
    , len = players.length
    , self = this
    , player
    ;

  pid = Number(pid);

  if (player = _.find(players, function(player)
    { return player.id === pid; })) {
    player.reset(ws, players.indexOf(player));
    this.meta();
    return;
  }
  if (len === this.size)
    return ws.error('q full');

  player = new Player(ws, pid, name, len, TIME);
  player.on('close', this.playerClose);
  player.on('meta', this.meta);
  player.on('pass', function(pack) {
    self.pass(pack, this);
  });

  len = players.push(player);
  if (len === this.size)
    this.start();
  else
    this.meta();
};

Q.prototype.playerClose = function(player) {
  var players = this.players;
  if (!this.started) {
    players.splice(players.indexOf(player), 1);
    _.each(players, function(player, index) {
      player.index = index;
    });
  }
  this.meta();
};

Q.prototype.pass = function(pack, player) {
  var players = this.players
    , len = players.length
    , delta = this.clockwise ? +1 : -1
    , idx = player.index + delta
    ;

  idx = (idx + len) % len; // negative modulo
  if (pack.cards.length)
    players[idx].receive(pack);
  else {
    if (!--this.openPacks)
      this.openPack();
  }
  this.meta();
};

Q.prototype.start = function() {
  this.started = true;
  if (this.type === 'sealed') {
    var sets = this.sets;
    this.ended = true;
    _.each(this.players, function(player) {
      var picks = _.chain(sets)
        .map(genPack)
        .pluck('cards')
        .flatten()
        .value()
      ;
      player.picks = picks;
      player.send('picks', picks);
    });
    this.meta();
  } else {
    this.intervalID = setInterval(this.decrement, 1000);
    this.openPack();
  }
};

Q.prototype.openPack = function() {
  var set = this.sets.shift();
  if (!set)
    return this.end();

  this.openPacks = this.size;
  this.clockwise = !this.clockwise;
  _.each(this.players, function(player) {
    player.receive(genPack(set));
  });
  this.meta();
};

Q.prototype.decrement = function() {
  _.each(this.players, function(player) {
    if (player.packs.length && !--player.time)
      player.autoPick();
  });
};

Q.prototype.end = function() {
  clearInterval(this.intervalID);
  this.ended = true;
  this.meta();
  setTimeout(this.end2, DELETE_DELAY);
};

Q.prototype.end2 = function() {
  _.invoke(this.players, 'end');
  this.emit('end'); // remove this q from router
};

Q.prototype.meta = function() {
  var players = this.players
    , arr = []
    , ended = this.ended
    , size = this.size
    ;
  _.each(players, function(player) {
    arr.push({
      dropped: player.dropped,
      hash: player.hash,
      name: player.name,
      packs: player.packs.length,
      time: player.time
    });
  });
  _.each(players, function(player, index) {
    var meta = {
      players: arr,
      ended: ended,
      index: index,
      size: size
    };
    player.send('meta', meta);
  });
};

module.exports = Q;
<|endoftext|>


<commit_before>import React from 'react';
import css from './Checkbox.css';
import classNames from 'classnames';

const propTypes = {
  checkedIcon: React.PropTypes.element,
  uncheckedIcon: React.PropTypes.element,
  tertiaryIcon: React.PropTypes.element,
};

class Checkbox extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      inputChecked: false,
      inputIndeterminate: false,
      inputInFocus: false,
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
  }

  getLabelStyle() {
    return classNames(
      { [`${css.checkboxLabel}`]: !this.props.labelClass },
      { [`${css[this.props.labelStyle]}`]: !this.props.labelClass && this.props.labelStyle },
      this.props.labelClass,
    );
  }

  getRootStyle() {
    let rootStyle = this.props.hover ? css.root + ' ' + css.rootHover : css.root;
    let hovered = '';
    if (this.props.hover && this.state.inputInFocus) {
      hovered = ' ' + css.hovered;
    }
    rootStyle += hovered;
    rootStyle += this.props.fullWidth ? ' ' + css.rootFullWidth : ' ';
    rootStyle += this.props.marginBottom0 ? ' ' + css.marginBottom0 : ' ';
    rootStyle += this.props.inline ? ' ' + css.inline : '';
    if (this.props.meta) {
      rootStyle += this.props.meta.error || this.props.meta.warning ? ' ' + css.checkboxHasFeedback : '';
    }

    return rootStyle;
  }

  getContainerStyle() {
    let containerStyle = css.root;
    containerStyle += this.props.fullWidth ? ' ' + css.rootFullWidth : ' ';
    containerStyle += this.props.marginBottom0 ? ' ' + css.marginBottom0 : ' ';
    containerStyle += this.props.inline ? ' ' + css.inline : '';
    return containerStyle;
  }

  getInputStyle() {
    const base = css.checkboxInput;
    let hidden = '';
    if (this.props.checkedIcon || this.props.uncheckedIcon || this.props.tertiaryIcon) {
      hidden = css.hidden;
    }
    return `${base} ${hidden}`;
  }

  componentDidMount() {
    this.setState({
      inputChecked: this.input.checked,
      inputIndeterminate: this.input.indeterminate,
    });
  }

  handleChange(e) {
    this.setState({
      inputChecked: this.input.checked,
      inputIndeterminate: this.input.indeterminate,
    });

    this.props.input ? this.props.input.onChange(e) : this.props.onChange(e);
  }

  handleFocus(e) {
    this.setState({
      inputInFocus: true,
    });

    this.props.input ? this.props.input.onFocus(e) : null;
    this.props.onFocus ? this.props.onFocus(e) : null;
  }

  handleBlur(e) {
    this.setState({
      inputInFocus: false,
    });

    this.props.input ? this.props.input.onBlur(e) : null;
    this.props.onBlur ? this.props.onBlur(e) : null;
  }

  render() {
    let cleanedProps;
    let inputProps;
    let inputChecked;
    if (this.props.input) {
      var { input, meta: { error, warning, touched }, ...rest } = this.props;
      var { onChange, onFocus, onBlur, value, ...inputAttr } = input;
      inputProps = { ...inputAttr };
      cleanedProps = { ...rest };
      inputChecked = value;
    } else {
      cleanedProps = this.props;
      inputProps = null;
    }

    const { label, required, fullWidth, marginBottom0, hover, checkedIcon, uncheckedIcon, tertiaryIcon, labelStyle, ...inputCustom } = cleanedProps;

    let warningElement;
    if (this.props.meta) {
      warningElement = touched && warning ?
{warning}
 : null;
    }

    let iconElement = null;
    if (checkedIcon || uncheckedIcon || tertiaryIcon) {
      let icon = null;
      let ariaStatus = null;
      if (this.state.inputChecked) {
        icon = this.props.checkedIcon;
        ariaStatus = "true";
      } else {
        icon = this.props.uncheckedIcon;
        ariaStatus = "false";
      }
      if (this.state.inputIndeterminate) {
        icon = this.props.tertiaryIcon;
        ariaStatus = "mixed";
      }
      iconElement = {icon};
    }

    let ariaProps = {};
    if (!this.props.name || !this.props.input) {
      ariaProps['aria-label'] = `checkbox ${this.props.label}`;
    }
    const { ...inputAriaProps } = ariaProps;

    return (

          <input type="checkbox" {...inputAriaProps} checked={inputChecked || inputCustom.checked}{...inputProps} {...inputCustom} className={this.getInputStyle()} onChange={this.handleChange} onFocus={this.handleFocus} onBlur={this.handleBlur} ref={(ref) => this.input = ref} />
          {iconElement}

            {label}

        {warningElement}

    );
  }
}

Checkbox.proptypes = propTypes;

export default Checkbox;
<commit_msg>quote cleanup. see STRPCOMP-21<commit_after>import React from 'react';
import css from './Checkbox.css';
import classNames from 'classnames';

const propTypes = {
  checkedIcon: React.PropTypes.element,
  uncheckedIcon: React.PropTypes.element,
  tertiaryIcon: React.PropTypes.element,
};

class Checkbox extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      inputChecked: false,
      inputIndeterminate: false,
      inputInFocus: false,
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
  }

  getLabelStyle() {
    return classNames(
      { [`${css.checkboxLabel}`]: !this.props.labelClass },
      { [`${css[this.props.labelStyle]}`]: !this.props.labelClass && this.props.labelStyle },
      this.props.labelClass,
    );
  }

  getRootStyle() {
    let rootStyle = this.props.hover ? css.root + ' ' + css.rootHover : css.root;
    let hovered = '';
    if (this.props.hover && this.state.inputInFocus) {
      hovered = ' ' + css.hovered;
    }
    rootStyle += hovered;
    rootStyle += this.props.fullWidth ? ' ' + css.rootFullWidth : ' ';
    rootStyle += this.props.marginBottom0 ? ' ' + css.marginBottom0 : ' ';
    rootStyle += this.props.inline ? ' ' + css.inline : '';
    if (this.props.meta) {
      rootStyle += this.props.meta.error || this.props.meta.warning ? ' ' + css.checkboxHasFeedback : '';
    }

    return rootStyle;
  }

  getContainerStyle() {
    let containerStyle = css.root;
    containerStyle += this.props.fullWidth ? ' ' + css.rootFullWidth : ' ';
    containerStyle += this.props.marginBottom0 ? ' ' + css.marginBottom0 : ' ';
    containerStyle += this.props.inline ? ' ' + css.inline : '';
    return containerStyle;
  }

  getInputStyle() {
    const base = css.checkboxInput;
    let hidden = '';
    if (this.props.checkedIcon || this.props.uncheckedIcon || this.props.tertiaryIcon) {
      hidden = css.hidden;
    }
    return `${base} ${hidden}`;
  }

  componentDidMount() {
    this.setState({
      inputChecked: this.input.checked,
      inputIndeterminate: this.input.indeterminate,
    });
  }

  handleChange(e) {
    this.setState({
      inputChecked: this.input.checked,
      inputIndeterminate: this.input.indeterminate,
    });

    this.props.input ? this.props.input.onChange(e) : this.props.onChange(e);
  }

  handleFocus(e) {
    this.setState({
      inputInFocus: true,
    });

    this.props.input ? this.props.input.onFocus(e) : null;
    this.props.onFocus ? this.props.onFocus(e) : null;
  }

  handleBlur(e) {
    this.setState({
      inputInFocus: false,
    });

    this.props.input ? this.props.input.onBlur(e) : null;
    this.props.onBlur ? this.props.onBlur(e) : null;
  }

  render() {
    let cleanedProps;
    let inputProps;
    let inputChecked;
    if (this.props.input) {
      var { input, meta: { error, warning, touched }, ...rest } = this.props;
      var { onChange, onFocus, onBlur, value, ...inputAttr } = input;
      inputProps = { ...inputAttr };
      cleanedProps = { ...rest };
      inputChecked = value;
    } else {
      cleanedProps = this.props;
      inputProps = null;
    }

    const { label, required, fullWidth, marginBottom0, hover, checkedIcon, uncheckedIcon, tertiaryIcon, labelStyle, ...inputCustom } = cleanedProps;

    let warningElement;
    if (this.props.meta) {
      warningElement = touched && warning ?
{warning}
 : null;
    }

    let iconElement = null;
    if (checkedIcon || uncheckedIcon || tertiaryIcon) {
      let icon = null;
      let ariaStatus = null;
      if (this.state.inputChecked) {
        icon = this.props.checkedIcon;
        ariaStatus = 'true';
      } else {
        icon = this.props.uncheckedIcon;
        ariaStatus = 'false';
      }
      if (this.state.inputIndeterminate) {
        icon = this.props.tertiaryIcon;
        ariaStatus = 'mixed';
      }
      iconElement = {icon};
    }

    let ariaProps = {};
    if (!this.props.name || !this.props.input) {
      ariaProps['aria-label'] = `checkbox ${this.props.label}`;
    }
    const { ...inputAriaProps } = ariaProps;

    return (

          <input type="checkbox" {...inputAriaProps} checked={inputChecked || inputCustom.checked}{...inputProps} {...inputCustom} className={this.getInputStyle()} onChange={this.handleChange} onFocus={this.handleFocus} onBlur={this.handleBlur} ref={(ref) => this.input = ref} />
          {iconElement}

            {label}

        {warningElement}

    );
  }
}

Checkbox.proptypes = propTypes;

export default Checkbox;
<|endoftext|>


<commit_before>const generator = require('yeoman-generator');
const chalk = require('chalk');
const prompts = require('./prompts.js');
const fs = require('fs');

const jhipsterConstants = require('../../node_modules/generator-jhipster/generators/generator-constants.js');
const jhipsterModuleSubgenerator = require('../../node_modules/generator-jhipster/generators/modules/index.js');
const dbh = require('../dbh.js');
const DBH_CONSTANTS = require('../dbh-constants.js');

// Stores JHipster variables
const jhipsterVar = {
    moduleName: DBH_CONSTANTS.fixEntityModuleName
};

// Stores JHipster functions
const jhipsterFunc = {};

// polyfill for jhipsterVar and jhipsterFunc when testing, see [issue #19](https://github.com/bastienmichaux/generator-jhipster-db-helper/issues/19)
let polyfill = {};

module.exports = generator.extend({
    /**
     * get a polyfill for the jhipsterVar and jhipsterFunc properties gone missing when testing
     * because of a [yeoman-test](https://github.com/bastienmichaux/generator-jhipster-db-helper/issues/19) issue
     *
     * @param {string} appConfigPath - path to the current .yo-rc.json application file
     */
    _getPolyfill: (appConfigPath) => {
        // stop if file not found
        if (!fs.existsSync(appConfigPath)) {
            throw new Error(`_getPolyfill: File ${appConfigPath} not found`);
        }

        // else return a promise holding the polyfill
        return dbh.getAppConfig(appConfigPath)
        .catch(err => console.error(err))
        .then((onResolve) => {
            const conf = onResolve['generator-jhipster'];
            const poly = {};

            // @todo: defensive programming with these properties (hasOwnProperty ? throw ?)

            // jhipsterVar polyfill :

            poly.jhipsterConfig = conf;
            poly.javaDir = `${jhipsterConstants.SERVER_MAIN_SRC_DIR + conf.packageFolder}/`;
            poly.resourceDir = jhipsterConstants.SERVER_MAIN_RES_DIR;
            poly.replaceContent = jhipsterModuleSubgenerator.prototype.replaceContent;
            poly.updateEntityConfig = jhipsterModuleSubgenerator.prototype.updateEntityConfig;

            // @todo : handle this.options.testMode ?

            return poly;
        }, onError => console.error(onError));
    },

    constructor: function (...args) { // eslint-disable-line object-shorthand
        generator.apply(this, args);
        // All information from entity generator
        this.entityConfig = this.options.entityConfig;
        this.entityTableName = this.options.entityConfig.entityTableName;
        this.entityClass = this.options.entityConfig.entityClass;
        this.fields = this.options.entityConfig.data.fields;
        this.relationships = this.options.entityConfig.data.relationships;
        this.force = this.options.force;

        // input from user (prompts.js will fill them)
        this.tableNameInput = null;
        this.columnsInput = [];
    },

    // check current project state, get configs, etc
    initializing() {
        this.log(chalk.bold.bgYellow('fix-entity generator'));
        this.log(chalk.bold.yellow('initializing'));

        this.composeWith('jhipster:modules',
            { jhipsterVar, jhipsterFunc },
            this.options.testmode ? { local: require.resolve('generator-jhipster/generators/modules') } : null
        );
        this.appConfig = jhipsterVar.jhipsterConfig;

        /* / TODO remove on prod
        this.prodDatabaseType = jhipsterVar.prodDatabaseType;
        this.log(chalk.blue('<<<<<BEFORE'));
        this.log(chalk.blue('entityConfig'));
        this.log(this.entityConfig);
        this.log(chalk.blue('fields'));
        this.log(this.fields);
        this.log(chalk.blue('relations'));
        this.log(this.options.entityConfig.data.relationships);
        this.log(chalk.blue('jhipsterVar'));
        this.log(jhipsterVar);
        //*/
    },

    // prompt the user for options
    prompting: {
        askForTableName: prompts.askForTableName,
        askForColumnsName: prompts.askForColumnsName
    },

    /**
     * After creating a new entity, replace the value of the table name.
     *
     * Allows consistent mapping with an existing database table without modifying JHipster's entity subgenerator.
     **/
    writing() {
        /**
         * Return path to the liquibase file corresponding to this entity and type of file.
         *
         * @param type is either 'entity' or 'entity_constraints'
         */
        const getLiquibaseFile = type => `${jhipsterVar.resourceDir}config/liquibase/changelog/${this.entityConfig.data.changelogDate}_added_${type}_${this.entityConfig.entityClass}.xml`;

        const files = {
            config: this.entityConfig.filename,
            ORM: `${jhipsterVar.javaDir}domain/${this.entityConfig.entityClass}.java`,
            liquibaseEntity: getLiquibaseFile('entity')
        };

        if (dbh.hasConstraints(this.relationships)) {
            files.liquibaseConstraints = getLiquibaseFile('entity_constraints');
        }

        // @todo it would be nice to move this procedure to dbh.js but it will loose access to jhipsterFunc
        /**
         * Update the value associated with the key. If the key doesn't exist yet, creates it.
         * To do so it checks the oldValue, undefined will be understood as if the key doesn't exist.
         *
         * @param landmark - the value beneath which it will add the key is not existent
         * @param key - the one it operates on
         * @param oldValue - the value associated to the key before the execution of this procedure (can be undefined)
         * @param newValue - the value to associate to the key, replace oldValue if any
         */
        const updateKey = (landmark, key, oldValue, newValue) => {
            if (oldValue === undefined) {
                // '(\\s*)' is for capturing indentation
                jhipsterFunc.replaceContent(files.config, `(\\s*)${landmark}`, `$1${landmark},$1"${key}": "${newValue}"`, true);
            } else {
                jhipsterFunc.replaceContent(files.config, `"${key}": "${oldValue}`, `"${key}": "${newValue}`);
            }
        };

        const replaceTableName = (paramFiles, newValue) => {
            jhipsterFunc.updateEntityConfig(paramFiles.config, 'entityTableName', newValue);

            // We search either for our value or jhipster value, so it works even if user didn't accept JHipster overwrite after a regeneration
            jhipsterFunc.replaceContent(paramFiles.ORM, `@Table(name = "${this.entityTableName}`, `@Table(name = "${newValue}`);
            jhipsterFunc.replaceContent(paramFiles.liquibaseEntity, `<createTable tableName="${this.entityTableName}`, `<createTable tableName="${newValue}`);
        };

        this.log(chalk.bold.yellow('writing'));

        // verify files exist
        for (const file in files) {
            // hasOwnProperty to avoid inherited properties
            if (files.hasOwnProperty(file) && !fs.existsSync(files[file])) {
                throw new Error(`JHipster-db-helper : File not found (${file}: ${files[file]}).`);
            }
        }

        /* // refactoring for later
        // verify files exist
        const filesArr = Object.keys(files);
        filesArr.forEach((file) => {
            if (!fs.existsSync(files[file])) {
                throw new Error(`JHipster-db-helper : File not found (${file}: ${files[file]}).`);
            }
        });
        */
        if(this.force) {
            this.tableNameInput = this.entityTableName;
            this.columnsInput = this.fields;
        }

        replaceTableName(files, this.tableNameInput);

        // Add/Change/Keep dbhColumnName for each field
        this.columnsInput.forEach((columnItem) => {
            const oldValue = columnItem.dbhColumnName;
            if(!oldValue && this.force) {
                throw new Error('You used option --force with bad configuration file, it needs dbhColumnName for each field');
            }
            const newValue = columnItem.columnNameInput || columnItem.dbhColumnName;

            updateKey(`"fieldName": "${columnItem.fieldName}"`, 'dbhColumnName', oldValue, newValue);

            // We search either for our value or JHipster value, so it works even if user didn't accept JHipster overwrite while regenerating
            jhipsterFunc.replaceContent(files.ORM, `@Column\\(name = "(${columnItem.fieldNameAsDatabaseColumn}|${oldValue})`, `@Column(name = "${newValue}`, true);
            jhipsterFunc.replaceContent(files.liquibaseEntity, `\\<column name="(${columnItem.fieldNameAsDatabaseColumn}|${oldValue})`, `<column name="${newValue}`, true);
        });

        // Add/Change/Keep dbhRelationshipId
        this.relationships.forEach((relationshipItem) => {
            const oldValue = relationshipItem.dbhRelationshipId;

            let columnName = null;
            let newValue = null;
            let initialTableIdName = null;

            if (relationshipItem.relationshipType === 'many-to-one' || (relationshipItem.relationshipType === 'one-to-one' && relationshipItem.ownerSide)) {
                columnName = dbh.getColumnIdName(relationshipItem.relationshipName);
                newValue = `${relationshipItem.relationshipName}_id`;

                jhipsterFunc.replaceContent(files.liquibaseConstraints, `baseTableName="${this.entityTableName}`, `baseTableName="${this.tableNameInput}`);
            } else if (relationshipItem.relationshipType === 'many-to-many' && relationshipItem.ownerSide) {
                columnName = dbh.getPluralColumnIdName(relationshipItem.relationshipName);
                newValue = `${relationshipItem.relationshipNamePlural}_id`;
                initialTableIdName = dbh.getPluralColumnIdName(this.entityClass);

                jhipsterFunc.replaceContent(files.liquibaseEntity, `\\<addPrimaryKey columnNames="${initialTableIdName}, (${columnName}|${oldValue})`, `<addPrimaryKey columnNames="${initialTableIdName}, ${newValue}`, true);
                jhipsterFunc.replaceContent(files.liquibaseConstraints, `referencedTableName="${this.entityTableName}`, `referencedTableName="${this.tableNameInput}`);
                jhipsterFunc.replaceContent(files.ORM, `inverseJoinColumns = @JoinColumn\\(name="(${columnName}|${oldValue})`, `inverseJoinColumns = @JoinColumn(name="${newValue}`, true);
            } else {
                // We don't need to do anything about relationships which don't add any constraint.
                return;
            }

            updateKey(`"relationshipName": "${relationshipItem.relationshipName}"`, 'dbhRelationshipId', oldValue, newValue);

            jhipsterFunc.replaceContent(files.liquibaseEntity, `\\<column name="(${columnName}|${oldValue})`, `<column name="${newValue}`, true);
            jhipsterFunc.replaceContent(files.liquibaseConstraints, `\\<addForeignKeyConstraint baseColumnNames="(${columnName}|${oldValue})`, `<addForeignKeyConstraint baseColumnNames="${newValue}`, true);
        });
    },

    // run installation (npm, bower, etc)
    install() {
        this.log(chalk.bold.yellow('install'));
    },

    // cleanup, say goodbye
    end() {
        this.log(chalk.bold.yellow('End of fix-entity generator'));
    }
});
<commit_msg>remove comment<commit_after>const generator = require('yeoman-generator');
const chalk = require('chalk');
const prompts = require('./prompts.js');
const fs = require('fs');

const jhipsterConstants = require('../../node_modules/generator-jhipster/generators/generator-constants.js');
const jhipsterModuleSubgenerator = require('../../node_modules/generator-jhipster/generators/modules/index.js');
const dbh = require('../dbh.js');
const DBH_CONSTANTS = require('../dbh-constants.js');

// Stores JHipster variables
const jhipsterVar = {
    moduleName: DBH_CONSTANTS.fixEntityModuleName
};

// Stores JHipster functions
const jhipsterFunc = {};

// polyfill for jhipsterVar and jhipsterFunc when testing, see [issue #19](https://github.com/bastienmichaux/generator-jhipster-db-helper/issues/19)
let polyfill = {};

module.exports = generator.extend({
    /**
     * get a polyfill for the jhipsterVar and jhipsterFunc properties gone missing when testing
     * because of a [yeoman-test](https://github.com/bastienmichaux/generator-jhipster-db-helper/issues/19) issue
     *
     * @param {string} appConfigPath - path to the current .yo-rc.json application file
     */
    _getPolyfill: (appConfigPath) => {
        // stop if file not found
        if (!fs.existsSync(appConfigPath)) {
            throw new Error(`_getPolyfill: File ${appConfigPath} not found`);
        }

        // else return a promise holding the polyfill
        return dbh.getAppConfig(appConfigPath)
        .catch(err => console.error(err))
        .then((onResolve) => {
            const conf = onResolve['generator-jhipster'];
            const poly = {};

            // @todo: defensive programming with these properties (hasOwnProperty ? throw ?)

            // jhipsterVar polyfill :

            poly.jhipsterConfig = conf;
            poly.javaDir = `${jhipsterConstants.SERVER_MAIN_SRC_DIR + conf.packageFolder}/`;
            poly.resourceDir = jhipsterConstants.SERVER_MAIN_RES_DIR;
            poly.replaceContent = jhipsterModuleSubgenerator.prototype.replaceContent;
            poly.updateEntityConfig = jhipsterModuleSubgenerator.prototype.updateEntityConfig;

            // @todo : handle this.options.testMode ?

            return poly;
        }, onError => console.error(onError));
    },

    constructor: function (...args) { // eslint-disable-line object-shorthand
        generator.apply(this, args);
        // All information from entity generator
        this.entityConfig = this.options.entityConfig;
        this.entityTableName = this.options.entityConfig.entityTableName;
        this.entityClass = this.options.entityConfig.entityClass;
        this.fields = this.options.entityConfig.data.fields;
        this.relationships = this.options.entityConfig.data.relationships;
        this.force = this.options.force;

        // input from user (prompts.js will fill them)
        this.tableNameInput = null;
        this.columnsInput = [];
    },

    // check current project state, get configs, etc
    initializing() {
        this.log(chalk.bold.bgYellow('fix-entity generator'));
        this.log(chalk.bold.yellow('initializing'));

        this.composeWith('jhipster:modules',
            { jhipsterVar, jhipsterFunc },
            this.options.testmode ? { local: require.resolve('generator-jhipster/generators/modules') } : null
        );
        this.appConfig = jhipsterVar.jhipsterConfig;

        /* / TODO remove on prod
        this.prodDatabaseType = jhipsterVar.prodDatabaseType;
        this.log(chalk.blue('<<<<<BEFORE'));
        this.log(chalk.blue('entityConfig'));
        this.log(this.entityConfig);
        this.log(chalk.blue('fields'));
        this.log(this.fields);
        this.log(chalk.blue('relations'));
        this.log(this.options.entityConfig.data.relationships);
        this.log(chalk.blue('jhipsterVar'));
        this.log(jhipsterVar);
        //*/
    },

    // prompt the user for options
    prompting: {
        askForTableName: prompts.askForTableName,
        askForColumnsName: prompts.askForColumnsName
    },

    /**
     * After creating a new entity, replace the value of the table name.
     *
     * Allows consistent mapping with an existing database table without modifying JHipster's entity subgenerator.
     **/
    writing() {
        /**
         * Return path to the liquibase file corresponding to this entity and type of file.
         *
         * @param type is either 'entity' or 'entity_constraints'
         */
        const getLiquibaseFile = type => `${jhipsterVar.resourceDir}config/liquibase/changelog/${this.entityConfig.data.changelogDate}_added_${type}_${this.entityConfig.entityClass}.xml`;

        const files = {
            config: this.entityConfig.filename,
            ORM: `${jhipsterVar.javaDir}domain/${this.entityConfig.entityClass}.java`,
            liquibaseEntity: getLiquibaseFile('entity')
        };

        if (dbh.hasConstraints(this.relationships)) {
            files.liquibaseConstraints = getLiquibaseFile('entity_constraints');
        }

        // @todo it would be nice to move this procedure to dbh.js but it will loose access to jhipsterFunc
        /**
         * Update the value associated with the key. If the key doesn't exist yet, creates it.
         * To do so it checks the oldValue, undefined will be understood as if the key doesn't exist.
         *
         * @param landmark - the value beneath which it will add the key is not existent
         * @param key - the one it operates on
         * @param oldValue - the value associated to the key before the execution of this procedure (can be undefined)
         * @param newValue - the value to associate to the key, replace oldValue if any
         */
        const updateKey = (landmark, key, oldValue, newValue) => {
            if (oldValue === undefined) {
                // '(\\s*)' is for capturing indentation
                jhipsterFunc.replaceContent(files.config, `(\\s*)${landmark}`, `$1${landmark},$1"${key}": "${newValue}"`, true);
            } else {
                jhipsterFunc.replaceContent(files.config, `"${key}": "${oldValue}`, `"${key}": "${newValue}`);
            }
        };

        const replaceTableName = (paramFiles, newValue) => {
            jhipsterFunc.updateEntityConfig(paramFiles.config, 'entityTableName', newValue);

            // We search either for our value or jhipster value, so it works even if user didn't accept JHipster overwrite after a regeneration
            jhipsterFunc.replaceContent(paramFiles.ORM, `@Table(name = "${this.entityTableName}`, `@Table(name = "${newValue}`);
            jhipsterFunc.replaceContent(paramFiles.liquibaseEntity, `<createTable tableName="${this.entityTableName}`, `<createTable tableName="${newValue}`);
        };

        this.log(chalk.bold.yellow('writing'));

        // verify files exist
        for (const file in files) {
            // hasOwnProperty to avoid inherited properties
            if (files.hasOwnProperty(file) && !fs.existsSync(files[file])) {
                throw new Error(`JHipster-db-helper : File not found (${file}: ${files[file]}).`);
            }
        }

        if (this.force) {
            this.tableNameInput = this.entityTableName;
            this.columnsInput = this.fields;
        }

        replaceTableName(files, this.tableNameInput);

        // Add/Change/Keep dbhColumnName for each field
        this.columnsInput.forEach((columnItem) => {
            const oldValue = columnItem.dbhColumnName;
            if (!oldValue && this.force) {
                throw new Error('You used option --force with bad configuration file, it needs dbhColumnName for each field');
            }
            const newValue = columnItem.columnNameInput || columnItem.dbhColumnName;

            updateKey(`"fieldName": "${columnItem.fieldName}"`, 'dbhColumnName', oldValue, newValue);

            // We search either for our value or JHipster value, so it works even if user didn't accept JHipster overwrite while regenerating
            jhipsterFunc.replaceContent(files.ORM, `@Column\\(name = "(${columnItem.fieldNameAsDatabaseColumn}|${oldValue})`, `@Column(name = "${newValue}`, true);
            jhipsterFunc.replaceContent(files.liquibaseEntity, `\\<column name="(${columnItem.fieldNameAsDatabaseColumn}|${oldValue})`, `<column name="${newValue}`, true);
        });

        // Add/Change/Keep dbhRelationshipId
        this.relationships.forEach((relationshipItem) => {
            const oldValue = relationshipItem.dbhRelationshipId;

            let columnName = null;
            let newValue = null;
            let initialTableIdName = null;

            if (relationshipItem.relationshipType === 'many-to-one' || (relationshipItem.relationshipType === 'one-to-one' && relationshipItem.ownerSide)) {
                columnName = dbh.getColumnIdName(relationshipItem.relationshipName);
                newValue = `${relationshipItem.relationshipName}_id`;

                jhipsterFunc.replaceContent(files.liquibaseConstraints, `baseTableName="${this.entityTableName}`, `baseTableName="${this.tableNameInput}`);
            } else if (relationshipItem.relationshipType === 'many-to-many' && relationshipItem.ownerSide) {
                columnName = dbh.getPluralColumnIdName(relationshipItem.relationshipName);
                newValue = `${relationshipItem.relationshipNamePlural}_id`;
                initialTableIdName = dbh.getPluralColumnIdName(this.entityClass);

                jhipsterFunc.replaceContent(files.liquibaseEntity, `\\<addPrimaryKey columnNames="${initialTableIdName}, (${columnName}|${oldValue})`, `<addPrimaryKey columnNames="${initialTableIdName}, ${newValue}`, true);
                jhipsterFunc.replaceContent(files.liquibaseConstraints, `referencedTableName="${this.entityTableName}`, `referencedTableName="${this.tableNameInput}`);
                jhipsterFunc.replaceContent(files.ORM, `inverseJoinColumns = @JoinColumn\\(name="(${columnName}|${oldValue})`, `inverseJoinColumns = @JoinColumn(name="${newValue}`, true);
            } else {
                // We don't need to do anything about relationships which don't add any constraint.
                return;
            }

            updateKey(`"relationshipName": "${relationshipItem.relationshipName}"`, 'dbhRelationshipId', oldValue, newValue);

            jhipsterFunc.replaceContent(files.liquibaseEntity, `\\<column name="(${columnName}|${oldValue})`, `<column name="${newValue}`, true);
            jhipsterFunc.replaceContent(files.liquibaseConstraints, `\\<addForeignKeyConstraint baseColumnNames="(${columnName}|${oldValue})`, `<addForeignKeyConstraint baseColumnNames="${newValue}`, true);
        });
    },

    // run installation (npm, bower, etc)
    install() {
        this.log(chalk.bold.yellow('install'));
    },

    // cleanup, say goodbye
    end() {
        this.log(chalk.bold.yellow('End of fix-entity generator'));
    }
});
<|endoftext|>


<commit_before>const generator = require('yeoman-generator'),
    chalk = require('chalk'),
    prompts = require('./prompts.js'),
    fs = require('fs');

const jhipsterVar = {
    moduleName: 'fix-entity'
};

const jhipsterFunc = {};

module.exports = generator.extend({
    constructor: function (...args) { // eslint-disable-line object-shorthand
        generator.apply(this, args);
        this.entityConfig = this.options.entityConfig;
        this.defaultTableName = this.options.entityConfig.entityClass;
        this.fields = this.options.entityConfig.data.fields;

        this.tableNameInput = null;
        this.columnsInput = [];
    },

    // check current project state, get configs, etc
    initializing() {
        const log = this.log; // TODO
        this.log('fix-entity generator');
        this.log('initializing');
        this.composeWith('jhipster:modules',
			{ jhipsterVar, jhipsterFunc },
			this.options.testmode ? { local: require.resolve('generator-jhipster/generators/modules') } : null
		);

		// TODO find column names and add them to this.fields
        // parse the json the see if there are any column name, if there is, add it to this.fields
        const jsonEntity = this.fs.readJSON('.jhipster/' + this.defaultTableName + '.json');

        this.log(chalk.red('PRINTING ' + this.defaultTableName + '.json')); // TODO
        this.log(typeof jsonEntity); // TODO
        this.log(jsonEntity); // TODO

        jsonEntity.fields.forEach(function (field) {
            log(field); // TODO
            if(field.columnName === undefined) {
                log(field.fieldName + ' has no associated columnName'); // TODO
                field.columnName = 'PREFIX_' + field.fieldName + '_SUFFIX';
            }
        });

        this.log(chalk.red('PRINTING ' + this.defaultTableName + '.json')); // TODO
        this.log(typeof jsonEntity); // TODO
        this.log(jsonEntity); // TODO

        jsonEntity.fields.forEach(function (field) {
            log(field); // TODO
            if(field.columnName === undefined) {
                log(field.fieldName + ' has no associated columnName'); // TODO
                field.columnName = 'pouet';
            } else {
                log(field.fieldName + ' has associated columnName : ' + field.columnName); // TODO
            }
        });
    },

    // prompt the user for options
    prompting: {
        askForTableName: prompts.askForTableName,
        askForColumnsName: prompts.askForColumnsName
    },

    // other Yeoman run loop steps would go here :

    // configuring() : Saving configurations and configure the project (creating .editorconfig files and other metadata files)

    // default() : If the method name doesn't match a priority, it will be pushed to this group.

    /**
     * After creating a new entity, replace the value of the table name.
     *
     * Allows consistent mapping with an existing database table without modifying JHipster's entity subgenerator.
     **/
    writing() {
        // DEBUG : log where we are
        this.log('writing');

        this.log(chalk.red('PRINTING FIELDS'));
        this.log(this.fields);

        /**
         *
         * @param files object which each member is a file in the form of {path: string, prefix: string, suffix: string}
         * @param userInput the value which will replace whatever is between the matching prefix and suffix for each file.
         */
        function injectUserInput(files, userInput) {
            /**
             * replaces the content of a file located by its prefix and suffix by a new value
             * doesn't take the old value into account
             *
             * @param file will be modified
             * @param prefix is before the value we want to change
             * @param suffix is after the value we want to change
             * @param value will replace the current value in between prefix and suffix
             */
            function replaceContent(file, prefix, suffix, value) {
                /**
                 * return a copy of str with all regex characters escaped
                 *
                 * @param str possibly contains regex characters
                 * @returns {XML|*|string|void}
                 */
                function escapeRegExp(str) {
                    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
                }
                jhipsterFunc.replaceContent(file, escapeRegExp(prefix) + '.*' + escapeRegExp(suffix), prefix + value + suffix, true);
            }

            // Replacing the values
            for (var file in files) {
                // hasOwnProperty to avoid inherited properties
                if (files.hasOwnProperty(file) && fs.existsSync(files[file].path)) {
                    replaceContent(files[file].path, files[file].prefix, files[file].suffix, userInput);
                } else if(files.hasOwnProperty(file)) {
                    throw new Error('File not found (' + file + ': ' + files[file].path + ')');
                }
            }

        }

        // update the entity json file
        jhipsterFunc.updateEntityConfig(this.entityConfig.filename, 'entityTableName', this.tableNameInput);

        // TODO dynamicly generate the replacement tasks

        // The files where we must change the value
        const files = {
            // path of the entity Java ORM
            // something like : src/main/java/package/domain/Foo.java
            ORMFile: {
                path: jhipsterVar.javaDir + '/domain/' + this.entityConfig.entityClass + '.java',
                prefix: '@Table(name = "',
                suffix: '")'
            },
            // path of the Liquibase changelog file
            // something like : src/main/resources/config/liquibase/changelog/20150128232313_added_entity_Foo.xml
            liquibaseFile: {
                path: jhipsterVar.resourceDir + 'config/liquibase/changelog/' + this.entityConfig.data.changelogDate + '_added_entity_' + this.entityConfig.entityClass + '.xml',
                prefix: '<createTable tableName="',
                suffix: '">'
            }
        };

        injectUserInput(files, this.tableNameInput);
		// TODO replace values
    },

    // conflict() : Where conflicts are handled (used internally)

    // run installation (npm, bower, etc)
    install() {
        // DEBUG : log where we are
        this.log('install');
    },

    // cleanup, say goodbye
    end() {
        // DEBUG : log where we are
        this.log('End of fix-entity generator');
    }
});
<commit_msg>pour bastien<commit_after>const generator = require('yeoman-generator'),
    chalk = require('chalk'),
    prompts = require('./prompts.js'),
    fs = require('fs');

const jhipsterVar = {
    moduleName: 'fix-entity'
};

const jhipsterFunc = {};

module.exports = generator.extend({
    constructor: function (...args) { // eslint-disable-line object-shorthand
        generator.apply(this, args);
        this.entityConfig = this.options.entityConfig;
        this.defaultTableName = this.options.entityConfig.entityClass;
        this.fields = this.options.entityConfig.data.fields;

        this.tableNameInput = null;
        this.columnsInput = [];
    },

    // check current project state, get configs, etc
    initializing() {
        const log = this.log; // TODO
        this.log('fix-entity generator');
        this.log('initializing');
        this.composeWith('jhipster:modules',
			{ jhipsterVar, jhipsterFunc },
			this.options.testmode ? { local: require.resolve('generator-jhipster/generators/modules') } : null
		);

		// TODO find column names and add them to this.fields
        // parse the json the see if there are any column name, if there is, add it to this.fields
        const jsonEntity = this.fs.readJSON('.jhipster/' + this.defaultTableName + '.json');

        this.log(chalk.red('PRINTING ' + this.defaultTableName + '.json')); // TODO
        this.log(jsonEntity); // TODO
        this.log(chalk.red('PRINTING fields')); // TODO
        this.log(this.fields); // TODO

        this.log(chalk.red('Searching column names')); // TODO
        jsonEntity.fields.forEach(function (field) {
            log(field); // TODO
            if(field.columnName === undefined) {
                // TODO do nothings
                log(field.fieldName + ' has no associated columnName'); // TODO
                field.columnName = 'PREFIX_' + field.fieldName + '_SUFFIX';
            } else {
                // TODO add a columnName entry in the corresponding field object from the array this.fields
                // select field from this.fields where field.fieldName LIKE jsonEntity.fieldName

            }
        });

        this.log(chalk.red('PRINTING ' + this.defaultTableName + '.json')); // TODO
        this.log(typeof jsonEntity); // TODO
        this.log(jsonEntity); // TODO

        this.log(chalk.red('Searching column names')); // TODO
        jsonEntity.fields.forEach(function (field) {
            log(field); // TODO
            if(field.columnName === undefined) {
                log(field.fieldName + ' has no associated columnName'); // TODO
                field.columnName = 'pouet';
            } else {
                log(field.fieldName + ' has associated columnName : ' + field.columnName); // TODO
            }
        });
    },

    // prompt the user for options
    prompting: {
        askForTableName: prompts.askForTableName,
        askForColumnsName: prompts.askForColumnsName
    },

    // other Yeoman run loop steps would go here :

    // configuring() : Saving configurations and configure the project (creating .editorconfig files and other metadata files)

    // default() : If the method name doesn't match a priority, it will be pushed to this group.

    /**
     * After creating a new entity, replace the value of the table name.
     *
     * Allows consistent mapping with an existing database table without modifying JHipster's entity subgenerator.
     **/
    writing() {
        // DEBUG : log where we are
        this.log('writing');

        this.log(chalk.red('PRINTING FIELDS'));
        this.log(this.fields);

        /**
         *
         * @param files object which each member is a file in the form of {path: string, prefix: string, suffix: string}
         * @param userInput the value which will replace whatever is between the matching prefix and suffix for each file.
         */
        function injectUserInput(files, userInput) {
            /**
             * replaces the content of a file located by its prefix and suffix by a new value
             * doesn't take the old value into account
             *
             * @param file will be modified
             * @param prefix is before the value we want to change
             * @param suffix is after the value we want to change
             * @param value will replace the current value in between prefix and suffix
             */
            function replaceContent(file, prefix, suffix, value) {
                /**
                 * return a copy of str with all regex characters escaped
                 *
                 * @param str possibly contains regex characters
                 * @returns {XML|*|string|void}
                 */
                function escapeRegExp(str) {
                    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
                }
                jhipsterFunc.replaceContent(file, escapeRegExp(prefix) + '.*' + escapeRegExp(suffix), prefix + value + suffix, true);
            }

            // Replacing the values
            for (var file in files) {
                // hasOwnProperty to avoid inherited properties
                if (files.hasOwnProperty(file) && fs.existsSync(files[file].path)) {
                    replaceContent(files[file].path, files[file].prefix, files[file].suffix, userInput);
                } else if(files.hasOwnProperty(file)) {
                    throw new Error('File not found (' + file + ': ' + files[file].path + ')');
                }
            }

        }

        // update the entity json file
        jhipsterFunc.updateEntityConfig(this.entityConfig.filename, 'entityTableName', this.tableNameInput);

        // TODO dynamicly generate the replacement tasks

        // The files where we must change the value
        const files = {
            // path of the entity Java ORM
            // something like : src/main/java/package/domain/Foo.java
            ORMFile: {
                path: jhipsterVar.javaDir + '/domain/' + this.entityConfig.entityClass + '.java',
                prefix: '@Table(name = "',
                suffix: '")'
            },
            // path of the Liquibase changelog file
            // something like : src/main/resources/config/liquibase/changelog/20150128232313_added_entity_Foo.xml
            liquibaseFile: {
                path: jhipsterVar.resourceDir + 'config/liquibase/changelog/' + this.entityConfig.data.changelogDate + '_added_entity_' + this.entityConfig.entityClass + '.xml',
                prefix: '<createTable tableName="',
                suffix: '">'
            }
        };

        injectUserInput(files, this.tableNameInput);
		// TODO replace values
    },

    // conflict() : Where conflicts are handled (used internally)

    // run installation (npm, bower, etc)
    install() {
        // DEBUG : log where we are
        this.log('install');
    },

    // cleanup, say goodbye
    end() {
        // DEBUG : log where we are
        this.log('End of fix-entity generator');
    }
});
<|endoftext|>