<commit_before>/*
    Custom code used to power credible sets demonstration example. This is not part of the core LocusZoom library,
    but can be included as a standalone file.

    The page must incorporate and load all libraries before this file can be used, including:
     - Vendor assets
     - LocusZoom
     - gwas-credible-sets (available via NPM or a related CDN)
*/
"use strict";

/* global gwasCredibleSets, LocusZoom */

if (typeof gwasCredibleSets === "undefined") {
    throw new Error("The standalone gwas-credible-sets library is required to use this extension");
}

// Specify a custom datasource that adds "credible sets" fields to the prepared API response
LocusZoom.KnownDataSources.extend("AssociationLZ", "CredibleAssociationLZ", {
    annotateData: function (records) {
        // Calculate raw bayes factors and posterior probabilities based on information returned from the API
        var nlogpvals = records.map(function (item) {
            return item["log_pvalue"];
        });
        var scores = gwasCredibleSets.scoring.bayesFactors(nlogpvals);
        var posteriorProbabilities = gwasCredibleSets.scoring.normalizeProbabilities(scores);

        // Use scores to mark the credible set in various ways (depending on your visualization preferences,
        //    some of these may be unneeded)
        var credibleSet = gwasCredibleSets.marking.findCredibleSet(scores);
        var credSetScaled = gwasCredibleSets.marking.rescaleCredibleSet(credibleSet);
        var credSetBool = gwasCredibleSets.marking.markBoolean(credibleSet);

        // Annotate each response record based on credible set membership
        records.forEach(function (item, index) {
            item["credibleSetPosteriorProb"] = posteriorProbabilities[index];
            item["credibleSetContribution"] = credSetScaled[index]; // Visualization helper: normalized to contribution within the set
            item["isCredible"] = credSetBool[index];
        });
        return records;
    }
});

LocusZoom.Layouts.add("tooltip", "association_credible_set", function () {
    // Extend a known tooltip with an extra row of info showing posterior probabilities
    var l = LocusZoom.Layouts.get("tooltip", "standard_association", { unnamespaced: true });
    l.html += "
Posterior probability: {{{{namespace[assoc]}}credibleSetPosteriorProb|scinotation}}";
    return l;
}());

LocusZoom.Layouts.add("tooltip", "annotation_credible_set", {
    namespace: {"assoc": "assoc"},
    closable: true,
    show: {or: ["highlighted", "selected"]},
    hide: {and: ["unhighlighted", "unselected"]},
    html: "{{{{namespace[assoc]}}variant}}
"
    + "P Value: {{{{namespace[assoc]}}log_pvalue|logtoscinotation}}
" +
    "
Posterior probability: {{{{namespace[assoc]}}credibleSetPosteriorProb|scinotation}}"
});

LocusZoom.Layouts.add("data_layer", "association_credible_set", function () {
    return LocusZoom.Layouts.get("data_layer", "association_pvalues", {
        unnamespaced: true,
        id: "associationcredibleset",
        fill_opacity: 0.7,
        tooltip: LocusZoom.Layouts.get("tooltip", "association_credible_set", { unnamespaced: true }),
        fields: [
            "{{namespace[assoc]}}variant", "{{namespace[assoc]}}position",
            "{{namespace[assoc]}}log_pvalue", "{{namespace[assoc]}}log_pvalue|logtoscinotation",
            "{{namespace[assoc]}}ref_allele", "{{namespace[assoc]}}credibleSetPosteriorProb",
            "{{namespace[assoc]}}credibleSetContribution", "{{namespace[assoc]}}isCredible",
            "{{namespace[ld]}}state", "{{namespace[ld]}}isrefvar"
        ]
    });
}());

LocusZoom.Layouts.add("data_layer", "annotation_credible_set", {
    namespace: {"assoc": "assoc"},
    id: "annotationcredibleset",
    type: "annotation_track",
    id_field: "{{namespace[assoc]}}variant",
    x_axis: {
        field: "{{namespace[assoc]}}position"
    },
    color: "#00CC00",
    // Credible set markings are derived fields. Although they don't need to be specified in the fields array,
    //  we DO need to specify the fields used to do the calculation (eg pvalue)
    fields: ["{{namespace[assoc]}}variant", "{{namespace[assoc]}}position", "{{namespace[assoc]}}log_pvalue", "{{namespace[assoc]}}credibleSetPosteriorProb", "{{namespace[assoc]}}credibleSetContribution", "{{namespace[assoc]}}isCredible"],
    filters: [
        // Specify which points to show on the track. Any selection must satisfy ALL filters
        ["{{namespace[assoc]}}isCredible", true]
    ],
    behaviors: {
        onmouseover: [
            {action: "set", status: "highlighted"}
        ],
        onmouseout: [
            {action: "unset", status: "highlighted"}
        ],
        onclick: [
            {action: "toggle", status: "selected", exclusive: true}
        ],
        onshiftclick: [
            {action: "toggle", status: "selected"}
        ]
    },
    tooltip: LocusZoom.Layouts.get("tooltip", "annotation_credible_set", { unnamespaced: true }),
    tooltip_positioning: "vertical"
});

LocusZoom.Layouts.add("panel", "annotation_credible_set", {
    id: "annotationcredibleset",
    title: { text: "SNPs in 95% credible set", x:50, style: { "font-size": "14px" } },
    width: 800,
    height: 100,
    min_height: 100,
    proportional_width: 1,
    margin: {top: 35, right: 50, bottom: 40, left: 50},
    inner_border: "rgb(210, 210, 210)",
    interaction: {
        drag_background_to_pan: true,
        scroll_to_zoom: true,
        x_linked: true
    },
    data_layers: [
        LocusZoom.Layouts.get("data_layer", "annotation_credible_set", {unnamespaced: true})
    ]
});

LocusZoom.Layouts.add("panel", "association_credible_set", function() {
    var l = LocusZoom.Layouts.get("panel", "association", {
        unnamespaced: true,
        id: "associationcrediblesets",
        namespace: { "assoc": "assoc" },
        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_credible_set", { unnamespaced: true })
        ]
    });
    // Add "display options" button to control how credible set coloring is overlaid on the standard association plot
    l.dashboard.components.push(
        {
            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: "associationcredibleset",
            default_config_display_name: "Linkage Disequilibrium (default)", // display name for the default plot color option (allow user to revert to plot defaults)

            options: [
                {
                    // First dropdown menu item
                    display_name: "95% credible set (boolean)",  // Human readable representation of field name
                    display: {  // Specify layout directives that control display of the plot for this option
                        point_shape: "circle",
                        point_size: 40,
                        color: {
                            field: "{{namespace[assoc]}}isCredible",
                            scale_function: "if",
                            parameters: {
                                field_value: true,
                                then: "#00CC00",
                                else: "#CCCCCC"
                            }
                        },
                        legend: [ // Tells the legend how to represent this display option
                            { shape: "circle", color: "#00CC00", size: 40, label: "In credible set", class: "lz-data_layer-scatter" },
                            { shape: "circle", color: "#CCCCCC", size: 40, label: "Not in credible set", class: "lz-data_layer-scatter" }
                        ]
                    }
                },
                {
                    // Second option. The same plot- or even the same field- can be colored in more than one way.
                    display_name: "95% credible set (gradient by contribution)",
                    display: {
                        point_shape: "circle",
                        point_size: 40,
                        color: [
                            {
                                field: "{{namespace[assoc]}}credibleSetContribution",
                                scale_function: "if",
                                parameters: {
                                    field_value: 0,
                                    then: "#777777"
                                }
                            },
                            {
                                scale_function: "interpolate",
                                field: "{{namespace[assoc]}}credibleSetContribution",
                                parameters: {
                                    breaks: [0, 1],
                                    values: ["#fafe87", "#9c0000"]
                                }
                            }
                        ],
                        legend: [
                            { shape: "circle", color: "#777777", size: 40, label: "No contribution", class: "lz-data_layer-scatter" },
                            { shape: "circle", color: "#fafe87", size: 40, label: "Some contribution", class: "lz-data_layer-scatter" },
                            { shape: "circle", color: "#9c0000", size: 40, label: "Most contribution", class: "lz-data_layer-scatter" }
                        ]
                    }
                }
            ]
        }
    );
    return l;
} ());

LocusZoom.Layouts.add("plot", "association_credible_set", {
    state: {},
    width: 800,
    height: 450,
    responsive_resize: true,
    min_region_scale: 20000,
    max_region_scale: 1000000,
    dashboard: LocusZoom.Layouts.get("dashboard", "standard_plot", {unnamespaced: true}),
    panels: [
        LocusZoom.Layouts.get("panel", "association_credible_set", {unnamespaced: true}),
        LocusZoom.Layouts.get("panel", "annotation_credible_set", {unnamespaced: true})
    ]
});
<commit_msg>Add a reference to the credible assoc source to the `ext.Data` namespace for debugging<commit_after>/*
    Custom code used to power credible sets demonstration example. This is not part of the core LocusZoom library,
    but can be included as a standalone file.

    The page must incorporate and load all libraries before this file can be used, including:
     - Vendor assets
     - LocusZoom
     - gwas-credible-sets (available via NPM or a related CDN)
*/
"use strict";

/* global gwasCredibleSets, LocusZoom */

if (typeof gwasCredibleSets === "undefined") {
    throw new Error("The standalone gwas-credible-sets library is required to use this extension");
}

!function() {
    if (!LocusZoom.ext.Data) {
        LocusZoom.ext.Data = {};
    }

    // Specify a custom datasource that adds "credible sets" fields to the prepared API response,
    //   and make it available as an "extension" symbol for later use
    LocusZoom.ext.Data.CredibleAssociationLZ = LocusZoom.KnownDataSources.extend("AssociationLZ", "CredibleAssociationLZ", {
        annotateData: function (records) {
            // Calculate raw bayes factors and posterior probabilities based on information returned from the API
            var nlogpvals = records.map(function (item) {
                return item["log_pvalue"];
            });
            var scores = gwasCredibleSets.scoring.bayesFactors(nlogpvals);
            var posteriorProbabilities = gwasCredibleSets.scoring.normalizeProbabilities(scores);

            // Use scores to mark the credible set in various ways (depending on your visualization preferences,
            //    some of these may be unneeded)
            var credibleSet = gwasCredibleSets.marking.findCredibleSet(scores);
            var credSetScaled = gwasCredibleSets.marking.rescaleCredibleSet(credibleSet);
            var credSetBool = gwasCredibleSets.marking.markBoolean(credibleSet);

            // Annotate each response record based on credible set membership
            records.forEach(function (item, index) {
                item["credibleSetPosteriorProb"] = posteriorProbabilities[index];
                item["credibleSetContribution"] = credSetScaled[index]; // Visualization helper: normalized to contribution within the set
                item["isCredible"] = credSetBool[index];
            });
            return records;
        }
    });

    // Add related layouts to the central global registry
    LocusZoom.Layouts.add("tooltip", "association_credible_set", function () {
        // Extend a known tooltip with an extra row of info showing posterior probabilities
        var l = LocusZoom.Layouts.get("tooltip", "standard_association", { unnamespaced: true });
        l.html += "
Posterior probability: {{{{namespace[assoc]}}credibleSetPosteriorProb|scinotation}}";
        return l;
    }());

    LocusZoom.Layouts.add("tooltip", "annotation_credible_set", {
        namespace: {"assoc": "assoc"},
        closable: true,
        show: {or: ["highlighted", "selected"]},
        hide: {and: ["unhighlighted", "unselected"]},
        html: "{{{{namespace[assoc]}}variant}}
"
        + "P Value: {{{{namespace[assoc]}}log_pvalue|logtoscinotation}}
" +
        "
Posterior probability: {{{{namespace[assoc]}}credibleSetPosteriorProb|scinotation}}"
    });

    LocusZoom.Layouts.add("data_layer", "association_credible_set", function () {
        return LocusZoom.Layouts.get("data_layer", "association_pvalues", {
            unnamespaced: true,
            id: "associationcredibleset",
            fill_opacity: 0.7,
            tooltip: LocusZoom.Layouts.get("tooltip", "association_credible_set", { unnamespaced: true }),
            fields: [
                "{{namespace[assoc]}}variant", "{{namespace[assoc]}}position",
                "{{namespace[assoc]}}log_pvalue", "{{namespace[assoc]}}log_pvalue|logtoscinotation",
                "{{namespace[assoc]}}ref_allele", "{{namespace[assoc]}}credibleSetPosteriorProb",
                "{{namespace[assoc]}}credibleSetContribution", "{{namespace[assoc]}}isCredible",
                "{{namespace[ld]}}state", "{{namespace[ld]}}isrefvar"
            ]
        });
    }());

    LocusZoom.Layouts.add("data_layer", "annotation_credible_set", {
        namespace: {"assoc": "assoc"},
        id: "annotationcredibleset",
        type: "annotation_track",
        id_field: "{{namespace[assoc]}}variant",
        x_axis: {
            field: "{{namespace[assoc]}}position"
        },
        color: "#00CC00",
        // Credible set markings are derived fields. Although they don't need to be specified in the fields array,
        //  we DO need to specify the fields used to do the calculation (eg pvalue)
        fields: ["{{namespace[assoc]}}variant", "{{namespace[assoc]}}position", "{{namespace[assoc]}}log_pvalue", "{{namespace[assoc]}}credibleSetPosteriorProb", "{{namespace[assoc]}}credibleSetContribution", "{{namespace[assoc]}}isCredible"],
        filters: [
            // Specify which points to show on the track. Any selection must satisfy ALL filters
            ["{{namespace[assoc]}}isCredible", true]
        ],
        behaviors: {
            onmouseover: [
                {action: "set", status: "highlighted"}
            ],
            onmouseout: [
                {action: "unset", status: "highlighted"}
            ],
            onclick: [
                {action: "toggle", status: "selected", exclusive: true}
            ],
            onshiftclick: [
                {action: "toggle", status: "selected"}
            ]
        },
        tooltip: LocusZoom.Layouts.get("tooltip", "annotation_credible_set", { unnamespaced: true }),
        tooltip_positioning: "vertical"
    });

    LocusZoom.Layouts.add("panel", "annotation_credible_set", {
        id: "annotationcredibleset",
        title: { text: "SNPs in 95% credible set", x:50, style: { "font-size": "14px" } },
        width: 800,
        height: 100,
        min_height: 100,
        proportional_width: 1,
        margin: {top: 35, right: 50, bottom: 40, left: 50},
        inner_border: "rgb(210, 210, 210)",
        interaction: {
            drag_background_to_pan: true,
            scroll_to_zoom: true,
            x_linked: true
        },
        data_layers: [
            LocusZoom.Layouts.get("data_layer", "annotation_credible_set", {unnamespaced: true})
        ]
    });

    LocusZoom.Layouts.add("panel", "association_credible_set", function() {
        var l = LocusZoom.Layouts.get("panel", "association", {
            unnamespaced: true,
            id: "associationcrediblesets",
            namespace: { "assoc": "assoc" },
            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_credible_set", { unnamespaced: true })
            ]
        });
        // Add "display options" button to control how credible set coloring is overlaid on the standard association plot
        l.dashboard.components.push(
            {
                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: "associationcredibleset",
                default_config_display_name: "Linkage Disequilibrium (default)", // display name for the default plot color option (allow user to revert to plot defaults)

                options: [
                    {
                        // First dropdown menu item
                        display_name: "95% credible set (boolean)",  // Human readable representation of field name
                        display: {  // Specify layout directives that control display of the plot for this option
                            point_shape: "circle",
                            point_size: 40,
                            color: {
                                field: "{{namespace[assoc]}}isCredible",
                                scale_function: "if",
                                parameters: {
                                    field_value: true,
                                    then: "#00CC00",
                                    else: "#CCCCCC"
                                }
                            },
                            legend: [ // Tells the legend how to represent this display option
                                { shape: "circle", color: "#00CC00", size: 40, label: "In credible set", class: "lz-data_layer-scatter" },
                                { shape: "circle", color: "#CCCCCC", size: 40, label: "Not in credible set", class: "lz-data_layer-scatter" }
                            ]
                        }
                    },
                    {
                        // Second option. The same plot- or even the same field- can be colored in more than one way.
                        display_name: "95% credible set (gradient by contribution)",
                        display: {
                            point_shape: "circle",
                            point_size: 40,
                            color: [
                                {
                                    field: "{{namespace[assoc]}}credibleSetContribution",
                                    scale_function: "if",
                                    parameters: {
                                        field_value: 0,
                                        then: "#777777"
                                    }
                                },
                                {
                                    scale_function: "interpolate",
                                    field: "{{namespace[assoc]}}credibleSetContribution",
                                    parameters: {
                                        breaks: [0, 1],
                                        values: ["#fafe87", "#9c0000"]
                                    }
                                }
                            ],
                            legend: [
                                { shape: "circle", color: "#777777", size: 40, label: "No contribution", class: "lz-data_layer-scatter" },
                                { shape: "circle", color: "#fafe87", size: 40, label: "Some contribution", class: "lz-data_layer-scatter" },
                                { shape: "circle", color: "#9c0000", size: 40, label: "Most contribution", class: "lz-data_layer-scatter" }
                            ]
                        }
                    }
                ]
            }
        );
        return l;
    } ());

    LocusZoom.Layouts.add("plot", "association_credible_set", {
        state: {},
        width: 800,
        height: 450,
        responsive_resize: true,
        min_region_scale: 20000,
        max_region_scale: 1000000,
        dashboard: LocusZoom.Layouts.get("dashboard", "standard_plot", {unnamespaced: true}),
        panels: [
            LocusZoom.Layouts.get("panel", "association_credible_set", {unnamespaced: true}),
            LocusZoom.Layouts.get("panel", "annotation_credible_set", {unnamespaced: true})
        ]
    });
}();

<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

export const BEZIER = {
  linear: 'linear',
  inQuad: 'cubic-bezier(0.55, 0.085, 0.68, 0.53)',
  outQuad: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
  inOutQuad: 'cubic-bezier(0.455, 0.03, 0.515, 0.955)',
  inCubic: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)',
  outCubic: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
  inOutCubic: 'cubic-bezier(0.645, 0.045, 0.355, 1)',
  inQuart: 'cubic-bezier(0.895, 0.03, 0.685, 0.22)',
  outQuart: 'cubic-bezier(0.165, 0.84, 0.44, 1)',
  inOutQuart: 'cubic-bezier(0.77, 0, 0.175, 1)',
  inQuint: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)',
  outQuint: 'cubic-bezier(0.23, 1, 0.32, 1)',
  inOutQuint: 'cubic-bezier(0.86, 0, 0.07, 1)',
  inSine: 'cubic-bezier(0.47, 0, 0.745, 0.715)',
  outSine: 'cubic-bezier(0.39, 0.575, 0.565, 1)',
  inOutSine: 'cubic-bezier(0.445, 0.05, 0.55, 0.95)',
  inExpo: 'cubic-bezier(0.95, 0.05, 0.795, 0.035)',
  outExpo: 'cubic-bezier(0.19, 1, 0.22, 1)',
  inOutExpo: 'cubic-bezier(1, 0, 0, 1)',
  inCirc: 'cubic-bezier(0.6, 0.04, 0.98, 0.335)',
  outCirc: 'cubic-bezier(0.075, 0.82, 0.165, 1)',
  inOutCirc: 'cubic-bezier(0.785, 0.135, 0.15, 0.86)',
};

export const ANIMATION_TYPES = {
  BLINK_ON: 'blinkOn',
  BOUNCE: 'bounce',
  FADE: 'fade',
  FLIP: 'flip',
  FLOAT_ON: 'floatOn',
  MOVE: 'move',
  PULSE: 'pulse',
  SPIN: 'spin',
  ZOOM: 'zoom',
};

export const ANIMATION_EFFECTS = {
  DROP: { value: 'effect-drop', name: __('Drop', 'web-stories') },
  FADE_IN: { value: 'effect-fade-in', name: __('Fade In', 'web-stories') },
  FLY_IN: { value: 'effect-fly-in', name: __('Fly In', 'web-stories') },
  PAN: { value: 'effect-pan', name: __('Pan', 'web-stories') },
  PULSE: { value: 'effect-pulse', name: __('Pulse', 'web-stories') },
  TWIRL_IN: { value: 'effect-twirl-in', name: __('Twirl In', 'web-stories') },
  WHOOSH_IN: {
    value: 'effect-whoosh-in',
    name: __('Whoosh In', 'web-stories'),
  },
  ZOOM: { value: 'effect-zoom', name: __('Zoom', 'web-stories') },
  ROTATE_IN: {
    value: 'effect-rotate-in',
    name: __('Rotate In', 'web-stories'),
  },
};

export const BACKGROUND_ANIMATION_EFFECTS = {
  ZOOM: { value: 'effect-background-zoom', name: ANIMATION_EFFECTS.ZOOM.name },
  PAN: { value: 'effect-background-pan', name: ANIMATION_EFFECTS.PAN.name },
};

export const ANIMATION_PARTS = {
  BLINK_ON: {
    value: ANIMATION_TYPES.BLINK_ON,
    name: __('Blink On', 'web-stories'),
  },
  BOUNCE: { value: ANIMATION_TYPES.BOUNCE, name: __('Bounce', 'web-stories') },
  FADE: { value: ANIMATION_TYPES.FADE, name: __('Fade', 'web-stories') },
  FLIP: { value: ANIMATION_TYPES.FLIP, name: __('Flip', 'web-stories') },
  FLOAT_ON: {
    value: ANIMATION_TYPES.FLOAT_ON,
    name: __('Float On', 'web-stories'),
  },
  MOVE: { value: ANIMATION_TYPES.MOVE, name: __('Move', 'web-stories') },
  PULSE: { value: ANIMATION_TYPES.PULSE, name: __('Pulse', 'web-stories') },
  SPIN: { value: ANIMATION_TYPES.SPIN, name: __('Spin', 'web-stories') },
  ZOOM: { value: ANIMATION_TYPES.ZOOM, name: __('Zoom', 'web-stories') },
};

export const DIRECTION = {
  TOP_TO_BOTTOM: 'topToBottom',
  RIGHT_TO_LEFT: 'rightToLeft',
  BOTTOM_TO_TOP: 'bottomToTop',
  LEFT_TO_RIGHT: 'leftToRight',
};

export const ROTATION = {
  CLOCKWISE: 'clockwise',
  COUNTER_CLOCKWISE: 'counterClockwise',
  PING_PONG: 'pingPong',
};

export const AXIS = {
  X: 'x',
  Y: 'y',
};

export const FIELD_TYPES = {
  DROPDOWN: 'dropdown',
  HIDDEN: 'hidden',
  ROTATION_PICKER: 'rotation_picker',
  DIRECTION_PICKER: 'direction_picker',
  NUMBER: 'number',
  FLOAT: 'float',
  TEXT: 'text',
  CHECKBOX: 'checkbox',
  RANGE: 'RANGE',
};

export const STORY_ANIMATION_STATE = {
  RESET: 'reset',
  PAUSED: 'paused',
  SCRUBBING: 'scrubbing',
  PLAYING: 'playing',
  PLAYING_SELECTED: 'playing-selected',
};

export const BG_MIN_SCALE = 100;
export const BG_MAX_SCALE = 400;
<commit_msg>renaming 'zoom' to 'scale' for the animation effect name so that the correct user facing label shows up when that animation is selected in the dropdown. Split effect name for background animation so that can still be 'zoom' independent of fg animations (#5527)<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

export const BEZIER = {
  linear: 'linear',
  inQuad: 'cubic-bezier(0.55, 0.085, 0.68, 0.53)',
  outQuad: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
  inOutQuad: 'cubic-bezier(0.455, 0.03, 0.515, 0.955)',
  inCubic: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)',
  outCubic: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
  inOutCubic: 'cubic-bezier(0.645, 0.045, 0.355, 1)',
  inQuart: 'cubic-bezier(0.895, 0.03, 0.685, 0.22)',
  outQuart: 'cubic-bezier(0.165, 0.84, 0.44, 1)',
  inOutQuart: 'cubic-bezier(0.77, 0, 0.175, 1)',
  inQuint: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)',
  outQuint: 'cubic-bezier(0.23, 1, 0.32, 1)',
  inOutQuint: 'cubic-bezier(0.86, 0, 0.07, 1)',
  inSine: 'cubic-bezier(0.47, 0, 0.745, 0.715)',
  outSine: 'cubic-bezier(0.39, 0.575, 0.565, 1)',
  inOutSine: 'cubic-bezier(0.445, 0.05, 0.55, 0.95)',
  inExpo: 'cubic-bezier(0.95, 0.05, 0.795, 0.035)',
  outExpo: 'cubic-bezier(0.19, 1, 0.22, 1)',
  inOutExpo: 'cubic-bezier(1, 0, 0, 1)',
  inCirc: 'cubic-bezier(0.6, 0.04, 0.98, 0.335)',
  outCirc: 'cubic-bezier(0.075, 0.82, 0.165, 1)',
  inOutCirc: 'cubic-bezier(0.785, 0.135, 0.15, 0.86)',
};

export const ANIMATION_TYPES = {
  BLINK_ON: 'blinkOn',
  BOUNCE: 'bounce',
  FADE: 'fade',
  FLIP: 'flip',
  FLOAT_ON: 'floatOn',
  MOVE: 'move',
  PULSE: 'pulse',
  SPIN: 'spin',
  ZOOM: 'zoom',
};

export const ANIMATION_EFFECTS = {
  DROP: { value: 'effect-drop', name: __('Drop', 'web-stories') },
  FADE_IN: { value: 'effect-fade-in', name: __('Fade In', 'web-stories') },
  FLY_IN: { value: 'effect-fly-in', name: __('Fly In', 'web-stories') },
  PAN: { value: 'effect-pan', name: __('Pan', 'web-stories') },
  PULSE: { value: 'effect-pulse', name: __('Pulse', 'web-stories') },
  TWIRL_IN: { value: 'effect-twirl-in', name: __('Twirl In', 'web-stories') },
  WHOOSH_IN: {
    value: 'effect-whoosh-in',
    name: __('Whoosh In', 'web-stories'),
  },
  ZOOM: { value: 'effect-zoom', name: __('Scale', 'web-stories') },
  ROTATE_IN: {
    value: 'effect-rotate-in',
    name: __('Rotate In', 'web-stories'),
  },
};

export const BACKGROUND_ANIMATION_EFFECTS = {
  ZOOM: {
    value: 'effect-background-zoom',
    name: __('Zoom', 'web-stories'),
  },
  PAN: { value: 'effect-background-pan', name: ANIMATION_EFFECTS.PAN.name },
};

export const ANIMATION_PARTS = {
  BLINK_ON: {
    value: ANIMATION_TYPES.BLINK_ON,
    name: __('Blink On', 'web-stories'),
  },
  BOUNCE: { value: ANIMATION_TYPES.BOUNCE, name: __('Bounce', 'web-stories') },
  FADE: { value: ANIMATION_TYPES.FADE, name: __('Fade', 'web-stories') },
  FLIP: { value: ANIMATION_TYPES.FLIP, name: __('Flip', 'web-stories') },
  FLOAT_ON: {
    value: ANIMATION_TYPES.FLOAT_ON,
    name: __('Float On', 'web-stories'),
  },
  MOVE: { value: ANIMATION_TYPES.MOVE, name: __('Move', 'web-stories') },
  PULSE: { value: ANIMATION_TYPES.PULSE, name: __('Pulse', 'web-stories') },
  SPIN: { value: ANIMATION_TYPES.SPIN, name: __('Spin', 'web-stories') },
  ZOOM: { value: ANIMATION_TYPES.ZOOM, name: __('Zoom', 'web-stories') },
};

export const DIRECTION = {
  TOP_TO_BOTTOM: 'topToBottom',
  RIGHT_TO_LEFT: 'rightToLeft',
  BOTTOM_TO_TOP: 'bottomToTop',
  LEFT_TO_RIGHT: 'leftToRight',
};

export const ROTATION = {
  CLOCKWISE: 'clockwise',
  COUNTER_CLOCKWISE: 'counterClockwise',
  PING_PONG: 'pingPong',
};

export const AXIS = {
  X: 'x',
  Y: 'y',
};

export const FIELD_TYPES = {
  DROPDOWN: 'dropdown',
  HIDDEN: 'hidden',
  ROTATION_PICKER: 'rotation_picker',
  DIRECTION_PICKER: 'direction_picker',
  NUMBER: 'number',
  FLOAT: 'float',
  TEXT: 'text',
  CHECKBOX: 'checkbox',
  RANGE: 'RANGE',
};

export const STORY_ANIMATION_STATE = {
  RESET: 'reset',
  PAUSED: 'paused',
  SCRUBBING: 'scrubbing',
  PLAYING: 'playing',
  PLAYING_SELECTED: 'playing-selected',
};

export const BG_MIN_SCALE = 100;
export const BG_MAX_SCALE = 400;
<|endoftext|>


<commit_before>'use strict';

import { h, Component } from 'preact';
import Client from '../lib/client.js';
import { bind } from 'decko';
import * as numbers from '../lib/numbers.js';
import * as d3 from 'd3';
import 'd3-transition';
d3.tip = require('d3-tip');

const formatMonth = d3.timeFormat("%b"),
  formatMonthDay = d3.timeFormat("%b %e");

const t = d3.transition().duration(600).ease(d3.easeQuadOut);
const xTickFormat = (len) => {
  return {
    hour: (d, i) => {
      if(len <= 24 && d.getHours() == 0 || d.getHours() == 12) {
        return d.getHours() + ":00";
      }

      if(i === 0 || i === len-1) {
        return formatMonthDay(d);
      }

      return '';
    },

    day: (d, i) => {
      if(i === 0 || i === len-1) {
        return formatMonthDay(d);
      }

      return '';
    },
    month: (d, i) => {
      if(len>24) {
        return d.getFullYear();
      }

      return d.getMonth() === 0 ? d.getFullYear() : formatMonth(d);
    }
  }
}

class Chart extends Component {
  constructor(props) {
    super(props)

    this.state = {
      loading: false,
      data: [],
      chartData: [],
      diffInDays: 1,
    }
  }

  componentWillReceiveProps(newProps) {
    let daysDiff = Math.round((newProps.dateRange[1]-newProps.dateRange[0])/1000/24/60/60);
    this.setState({
      diffInDays: daysDiff,
      tickStep: newProps.tickStep,
    })

    if( newProps.siteId != this.props.siteId || newProps.dateRange[0] != this.props.dateRange[0] || newProps.dateRange[1] != this.props.dateRange[1] ) {
      this.fetchData(newProps)
    } else if (newProps.tickStep != this.props.tickStep) {
      this.chartData()
      this.redrawChart()
    }
  }

  @bind
  chartData() {
    let startDate = this.props.dateRange[0];
    let endDate = this.props.dateRange[1];
    let newData = [];

    // instantiate JS Date objects
    let data = this.state.data.map(d => {
      d.Date = new Date(d.Date);
      return d
    })

    // make sure we have values for each date (so 0 value for gaps)
    let currentDate = startDate, nextDate, tick, offset = 0;
    while(currentDate < endDate) {
      tick = {
          "Pageviews": 0,
          "Visitors": 0,
          "Date": new Date(currentDate),
      };

      nextDate = new Date(currentDate)

      switch(this.state.tickStep) {
        case 'hour':
        nextDate.setHours(nextDate.getHours() + 1);
        break;

        case 'day':
        nextDate.setDate(nextDate.getDate() + 1);
        break;

        case 'month':
        nextDate.setMonth(nextDate.getMonth() + 1);
        break;
      }

      // grab data that falls between currentDate & nextDate
      for(let i=data.length-offset-1; i>=0; i--) {
        // Because 9AM should be included in 9AM-10AM range, check for equality here
        if( data[i].Date >= nextDate) {
          break;
        }

         // increment offset so subsequent dates can skip first X items in array
         offset += 1;

        // continue to next item in array if we're still below our target date
        if( data[i].Date < currentDate) {
          continue;
        }

        // add to tick data
        tick.Pageviews += data[i].Pageviews;
        tick.Visitors += data[i].Visitors;
      }

      newData.push(tick);
      currentDate = nextDate;
    }

    this.setState({
      chartData: newData,
    })
  }

  @bind
  prepareChart() {
    let padding = { top: 12, right: 12, bottom: 24, left: 40 };
    let height = 240;
    let width = this.base.clientWidth;

    this.innerWidth = width - padding.left - padding.right;
    this.innerHeight = height - padding.top - padding.bottom;

    this.ctx =  d3.select(this.base)
      .append('svg')
      .attr('width', width)
      .attr('height', height)
      .append('g')
      .attr('transform', 'translate(' + padding.left + ', '+padding.top+')')

    this.x = d3.scaleBand().range([0, this.innerWidth]).padding(0.1)
    this.y = d3.scaleLinear().range([this.innerHeight, 0])

      // tooltip
    this.tip = d3.tip().attr('class', 'd3-tip').html((d) => {
      let title =  d.Date.toLocaleDateString();

      if(this.state.tickStep === 'hour') {
        title += ` ${d.Date.getHours()}:00 - ${d.Date.getHours() + 1}:00`
      }

      return (`
${title}

${d.Pageviews}

Pageviews

${d.Visitors}

Visitors

`)});
    this.ctx.call(this.tip)
  }

  @bind
  redrawChart() {
    let data = this.state.chartData;

    if( ! this.ctx ) {
      this.prepareChart()
    }

    let graph = this.ctx;
    let innerWidth = this.innerWidth
    let innerHeight = this.innerHeight
    const max = d3.max(data, d => d.Pageviews);
    let x = this.x.domain(data.map(d => d.Date))
    let y = this.y.domain([0, max*1.1])
    let yAxis = d3.axisLeft().scale(y).ticks(3).tickSize(-innerWidth).tickFormat(v => numbers.formatPretty(v))
    let xAxis = d3.axisBottom().scale(x).tickFormat(xTickFormat(data.length)[this.state.tickStep])

    // only show first and last tick if we have more than 24 ticks to show
    if(data.length > 24) {
      xAxis.tickValues(data.map(d => d.Date).filter((d, i) => i === 0 || i === data.length-1))
    }

    // empty previous graph
    graph.selectAll('*').remove()

    // add text indicating there's no data yet
    if( max === 0 ) {
      graph.append('text')
        .attr('class', 'muted')
        .attr("text-anchor", "middle")
        .attr('x', innerWidth / 2 - 30)
        .attr('y', innerHeight / 2)
        .text('Nothing here, yet.')
    }

    // add axes
    let yTicks = graph.append("g")
      .attr("class", "y axis")
      .call(yAxis);

    let xTicks = graph.append("g")
      .attr("class", "x axis")
      .attr('transform', 'translate(0,' + innerHeight + ')')
      .call(xAxis)

    // add data for each tick that we have something to show for
    let barWidth = x.bandwidth()
    let ticks = graph.selectAll('.item')
      .data(data.filter(d => d.Pageviews > 0 || d.Visitors > 0)).enter()
      .append('g')
      .attr('class', 'item')

    let pageviews = ticks.append('rect')
      .attr('class', 'bar-pageviews')
      .attr('x', d => x(d.Date))
      .attr('width', barWidth)
      .attr("y", innerHeight)
      .attr("height", 0)

    let visitors = ticks.append('rect')
      .attr('class', 'bar-visitors')
      .attr('x', d => x(d.Date) )
      .attr('width', barWidth)
      .attr("y", innerHeight)
      .attr("height", 0)

    pageviews.transition(t)
      .attr('y', d => y(d.Pageviews))
      .attr('height', (d) => innerHeight - y(d.Pageviews))

    visitors.transition(t)
      .attr('height', (d) => (innerHeight - y(d.Visitors)) )
      .attr('y', (d) => y(d.Visitors))

    // add event listeners for tooltips
    ticks.on('mouseover', this.tip.show).on('mouseout', this.tip.hide)
  }

  @bind
  fetchData(props) {
    this.setState({ loading: true })

    let before = props.dateRange[1]/1000;
    let after = props.dateRange[0]/1000;

    Client.request(`/sites/${props.siteId}/stats/site?before=${before}&after=${after}`)
      .then(data => {

        this.setState({
          loading: false,
          data: data,
        })

        this.chartData()
        this.redrawChart()
      })
  }

  render(props, state) {
    return (

    )
  }
}

export default Chart
<commit_msg>fix to 1st of month when grouping chart by month<commit_after>'use strict';

import { h, Component } from 'preact';
import Client from '../lib/client.js';
import { bind } from 'decko';
import * as numbers from '../lib/numbers.js';
import * as d3 from 'd3';
import 'd3-transition';
d3.tip = require('d3-tip');

const formatMonth = d3.timeFormat("%b"),
  formatMonthDay = d3.timeFormat("%b %e");

const t = d3.transition().duration(600).ease(d3.easeQuadOut);
const xTickFormat = (len) => {
  return {
    hour: (d, i) => {
      if(len <= 24 && d.getHours() == 0 || d.getHours() == 12) {
        return d.getHours() + ":00";
      }

      if(i === 0 || i === len-1) {
        return formatMonthDay(d);
      }

      return '';
    },

    day: (d, i) => {
      if(i === 0 || i === len-1) {
        return formatMonthDay(d);
      }

      return '';
    },
    month: (d, i) => {
      if(len>24) {
        return d.getFullYear();
      }

      return d.getMonth() === 0 ? d.getFullYear() : formatMonth(d);
    }
  }
}

class Chart extends Component {
  constructor(props) {
    super(props)

    this.state = {
      loading: false,
      data: [],
      chartData: [],
      diffInDays: 1,
    }
  }

  componentWillReceiveProps(newProps) {
    let daysDiff = Math.round((newProps.dateRange[1]-newProps.dateRange[0])/1000/24/60/60);

    this.setState({
      diffInDays: daysDiff,
      tickStep: newProps.tickStep,
    })

    if( newProps.siteId != this.props.siteId || newProps.dateRange[0] != this.props.dateRange[0] || newProps.dateRange[1] != this.props.dateRange[1] ) {
      this.fetchData(newProps)
    } else if (newProps.tickStep != this.props.tickStep) {
      this.chartData()
      this.redrawChart()
    }
  }

  @bind
  chartData() {
    let startDate = new Date(this.props.dateRange[0]);
    let endDate = this.props.dateRange[1];
    let newData = [];

    // if grouping by month, fix date to 1st of month
    if(this.state.tickStep === 'month') {
      startDate.setDate(1);
    }

    // instantiate JS Date objects
    let data = this.state.data.map(d => {
      d.Date = new Date(d.Date);
      return d
    })

    // make sure we have values for each date (so 0 value for gaps)
    let currentDate = startDate, nextDate, tick, offset = 0;
    while(currentDate < endDate) {
      tick = {
          "Pageviews": 0,
          "Visitors": 0,
          "Date": new Date(currentDate),
      };

      nextDate = new Date(currentDate)

      switch(this.state.tickStep) {
        case 'hour':
        nextDate.setHours(nextDate.getHours() + 1);
        break;

        case 'day':
        nextDate.setDate(nextDate.getDate() + 1);
        break;

        case 'month':
        nextDate.setMonth(nextDate.getMonth() + 1);
        break;
      }

      // grab data that falls between currentDate & nextDate
      for(let i=data.length-offset-1; i>=0; i--) {
        // Because 9AM should be included in 9AM-10AM range, check for equality here
        if( data[i].Date >= nextDate) {
          break;
        }

         // increment offset so subsequent dates can skip first X items in array
         offset += 1;

        // continue to next item in array if we're still below our target date
        if( data[i].Date < currentDate) {
          continue;
        }

        // add to tick data
        tick.Pageviews += data[i].Pageviews;
        tick.Visitors += data[i].Visitors;
      }

      newData.push(tick);
      currentDate = nextDate;
    }

    this.setState({
      chartData: newData,
    })
  }

  @bind
  prepareChart() {
    let padding = { top: 12, right: 12, bottom: 24, left: 40 };
    let height = 240;
    let width = this.base.clientWidth;

    this.innerWidth = width - padding.left - padding.right;
    this.innerHeight = height - padding.top - padding.bottom;

    this.ctx =  d3.select(this.base)
      .append('svg')
      .attr('width', width)
      .attr('height', height)
      .append('g')
      .attr('transform', 'translate(' + padding.left + ', '+padding.top+')')

    this.x = d3.scaleBand().range([0, this.innerWidth]).padding(0.1)
    this.y = d3.scaleLinear().range([this.innerHeight, 0])

      // tooltip
    this.tip = d3.tip().attr('class', 'd3-tip').html((d) => {
      let title =  d.Date.toLocaleDateString();

      if(this.state.tickStep === 'hour') {
        title += ` ${d.Date.getHours()}:00 - ${d.Date.getHours() + 1}:00`
      }

      return (`

${title}

${d.Pageviews}

Pageviews

${d.Visitors}

Visitors

`
      )});

    this.ctx.call(this.tip)
  }

  @bind
  redrawChart() {
    let data = this.state.chartData;

    if( ! this.ctx ) {
      this.prepareChart()
    }

    let graph = this.ctx;
    let innerWidth = this.innerWidth
    let innerHeight = this.innerHeight
    const max = d3.max(data, d => d.Pageviews);
    let x = this.x.domain(data.map(d => d.Date))
    let y = this.y.domain([0, max*1.1])
    let yAxis = d3.axisLeft().scale(y).ticks(3).tickSize(-innerWidth).tickFormat(v => numbers.formatPretty(v))
    let xAxis = d3.axisBottom().scale(x).tickFormat(xTickFormat(data.length)[this.state.tickStep])

    // only show first and last tick if we have more than 24 ticks to show
    if(data.length > 24) {
      xAxis.tickValues(data.map(d => d.Date).filter((d, i) => i === 0 || i === data.length-1))
    }

    // empty previous graph
    graph.selectAll('*').remove()

    // add text indicating there's no data yet
    if( max === 0 ) {
      graph.append('text')
        .attr('class', 'muted')
        .attr("text-anchor", "middle")
        .attr('x', innerWidth / 2 - 30)
        .attr('y', innerHeight / 2)
        .text('Nothing here, yet.')
    }

    // add axes
    let yTicks = graph.append("g")
      .attr("class", "y axis")
      .call(yAxis);

    let xTicks = graph.append("g")
      .attr("class", "x axis")
      .attr('transform', 'translate(0,' + innerHeight + ')')
      .call(xAxis)

    // add data for each tick that we have something to show for
    let barWidth = x.bandwidth()
    let ticks = graph.selectAll('.item')
      .data(data.filter(d => d.Pageviews > 0 || d.Visitors > 0)).enter()
      .append('g')
      .attr('class', 'item')

    let pageviews = ticks.append('rect')
      .attr('class', 'bar-pageviews')
      .attr('x', d => x(d.Date))
      .attr('width', barWidth)
      .attr("y", innerHeight)
      .attr("height", 0)

    let visitors = ticks.append('rect')
      .attr('class', 'bar-visitors')
      .attr('x', d => x(d.Date) )
      .attr('width', barWidth)
      .attr("y", innerHeight)
      .attr("height", 0)

    pageviews.transition(t)
      .attr('y', d => y(d.Pageviews))
      .attr('height', (d) => innerHeight - y(d.Pageviews))

    visitors.transition(t)
      .attr('height', (d) => (innerHeight - y(d.Visitors)) )
      .attr('y', (d) => y(d.Visitors))

    // add event listeners for tooltips
    ticks.on('mouseover', this.tip.show).on('mouseout', this.tip.hide)
  }

  @bind
  fetchData(props) {
    this.setState({ loading: true })

    let before = props.dateRange[1]/1000;
    let after = props.dateRange[0]/1000;

    Client.request(`/sites/${props.siteId}/stats/site?before=${before}&after=${after}`)
      .then(data => {

        this.setState({
          loading: false,
          data: data,
        })

        this.chartData()
        this.redrawChart()
      })
  }

  render(props, state) {
    return (

    )
  }
}

export default Chart
<|endoftext|>


<commit_before>
export { Base };

/**
 * Testing Filters of multiple layers.
 *
 * @member {string} name This test filters' name.
 */
class Base {

  /**
   * @param sourceHeight      The height (and width) of the source image which will be processed by apply().
   * @param sourceDepth       The channel count of the source image.
   * @param targetHeight      The taregt image height (and width).
   * @param filterHeight      The height (and width) of each depthwise convolution.
   *
   * @param {number} channelMultiplierBlock0
   *   The first block (Block 0) will expand input channel by this factor.
   *
   * @param {number} stepCountPerBlock
   *   If zero or negative (<= 0), every block will use only one tf.depthwiseConv2d( strides = 1, pad = "valid" ) for shrinking sourceHeight
   * (minus ( filterHeight - 1 )). If positive (>= 1), every block will use one tf.depthwiseConv2d( strides = 2, pad = "same" ) to shrink
   * (half downsample) and use ( stageCountPerBlock - 1 ) times tf.depthwiseConv2d( strides = 1, pad = "same" ) until the block end.
   *
   * @param strAvgMaxConv
   *   Depthwise operation. "Avg" or "Max" or "Conv" for average pooling, max pooling, depthwise convolution.
   *
   * @param bDepthwiseBias
   *   If true, there will be a bias after depthwise convolution.
   *
   * @param depthwiseActivationName
   *   The activation function name after depthwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   *
   * @param bPointwise
   *   If true, there will be pointwise convolution after every layer of depthwise convolution.
   *
   * @param bPointwiseBias
   *   If true, there will be a bias after pointwise convolution. If ( bPointwise == false ), this will be also ignored.
   *
   * @param pointwiseActivationName
   *   The activation function name after pointwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   * If ( bPointwise == false ), this activation function will be also ignored.
   */
  init(
    sourceHeight, sourceDepth, targetHeight,
    filterHeight, channelMultiplierBlock0,
    stepCountPerBlock,
    strAvgMaxConv, bDepthwiseBias, depthwiseActivationName,
    bPointwise, bPointwiseBias, pointwiseActivationName ) {

    this.disposeTensors();

    this.stepCountPerBlock = stepCountPerBlock;

    let differenceHeight = sourceHeight - targetHeight;
    let filterWidth = filterHeight;

    this.channelMultiplier = channelMultiplierBlock0;
    this.channelCountBlock0 = sourceDepth * channelMultiplierBlock0;  // the channel count of the first block (Block 0).

    this.strAvgMaxConv = strAvgMaxConv;
    switch ( strAvgMaxConv ) {
      case "Avg":  this.bDepthwiseAvg = true;
      case "Max":  this.bDepthwiseMax = true;
      case "Conv": this.bDepthwiseConv = true;
    }

    this.bDepthwiseBias = bDepthwiseBias;

    this.depthwiseActivationName = depthwiseActivationName;
    switch ( depthwiseActivationName ) {
      case "relu":    this.depthwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.depthwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.depthwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.depthwiseActivationFunction = tf.tanh;    break;
      case "sin":     this.depthwiseActivationFunction = tf.sin;     break;
      //default:
    }

    this.bPointwise = bPointwise;
    this.bPointwiseBias = bPointwiseBias;

    this.pointwiseActivationName = pointwiseActivationName;
    switch ( pointwiseActivationName ) {
      case "relu":    this.pointwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.pointwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.pointwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.pointwiseActivationFunction = tf.tanh;    break;
      case "sin":     this.pointwiseActivationFunction = tf.sin;     break;
      //default:
    }

    // The height of processed image will be reduced a little for any depthwise filter larger than 1x1.
    let heightReducedPerStep = filterHeight - 1;

    if ( stepCountPerBlock >= 1 ) {
      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 2, pad = "same" ).
      this.blockCount = Math.floor( Math.log2( sourceHeight ) );
    } else {
      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 1, pad = "valid" ).
      this.blockCount = Math.floor( differenceHeight / heightReducedPerStep );
    }

    // e.g. "C24_24__DConv_101x101_DBias_RELU__PConv_PBias_RELU__Block_1__Step_1"
    this.name = `C${sourceDepth}_${this.channelCountBlock0}`
      + `__D${strAvgMaxConv}_${filterHeight}x${filterHeight}`
      + `${ ( this.bDepthwiseBias ) ? ( "_DBias" ) : "" }`
      + `${ ( this.depthwiseActivationFunction ) ? ( "_" + depthwiseActivationName ) : "" }`
      + `${ ( this.bPointwise ) ? "__PConv" : "" }`
      + `${ ( this.bPointwise && this.bPointwiseBias ) ? ( "_PBias" ) : "" }`
      + `${ ( this.bPointwise && this.pointwiseActivationFunction ) ? ( "_" + pointwiseActivationName ) : "" }`
      + `__Block_${this.blockCount}`
      + `__Step${stepCountPerBlock}`
    ;

    // Depthwise Filters and Biases

    this.depthwiseFilterHeightWidth = [ filterHeight, filterWidth ];

    // First block's depthwise filters will expand channel count from sourceDepth to ( sourceDepth * channelMultiplierBlock0 ).
    this.depthwiseFiltersShapeFirst = [ filterHeight, filterWidth,             sourceDepth, channelMultiplierBlock0 ];
    this.depthwiseFiltersShape =      [ filterHeight, filterWidth, this.channelCountBlock0,                       1 ];
    this.depthwiseBiasesShape =       [            1,           1, this.channelCountBlock0 ];

    // Every element (Tensor4d) is a depthwiseFilters for one block.
    this.depthwiseFiltersTensor4dArray
      = Base.generateTensorArray( this.blockCount, this.depthwiseFiltersShapeFirst, this.depthwiseFiltersShape, this.bDepthwiseConv );

    // Every element (Tensor3d) is a depthwiseBiases for one block.
    this.depthwiseBiasesTensor3dArray
      = Base.generateTensorArray( this.blockCount, null, this.depthwiseBiasesShape, ( this.bDepthwiseConv && bDepthwiseBias ) );

    // Pointwise Filters and Biases

    let pointwiseInputDepth =  this.channelCountBlock0;
    let pointwiseOutputDepth = this.channelCountBlock0; // Assume output depth is the same as input.

    this.pointwiseFilterHeightWidth = [ 1, 1 ];
    this.pointwiseFiltersShape = [ 1, 1, pointwiseInputDepth, pointwiseOutputDepth ];

    // Both input depth and output depth of pointwise bias are the same as pointwise convolution output.
    this.pointwiseBiasesShape =  [ 1, 1, pointwiseOutputDepth ];

    // Every element (Tensor4d) is a pointwiseFilters for one block.
    this.pointwiseFiltersTensor4dArray
      = Base.generateTensorArray( this.blockCount, null, this.pointwiseFiltersShape, bPointwise );

    // Every element (Tensor3d) is a pointwiseBiases for one block.
    this.pointwiseBiasesTensor3dArray
      = Base.generateTensorArray( this.blockCount, null, this.pointwiseBiasesShape, ( bPointwise && bPointwiseBias ) );
  }

  /**
   * @param {number}   blockCount          The element count (i.e. length) of the returned array.
   * @param {number[]} newTensorShapeFirst The tensor's shape of first element of the returned array. If null, same as newTensorShape.
   * @param {number[]} newTensorShape      The tensor's shape of every (except first) element of the returned array.
   * @param {boolean}  bNullElement        If true, every element of the return array will be null.
   *
   * @return {tf.tensor4d[]|tf.tensor3d[]}
   *   Return a array whose every element is a tensor4d or tensor3d (for one block), or null (if ( bNullElement == true ) ).
   */
  static generateTensorArray( blockCount, newTensorShapeFirst, newTensorShape, bNullElement ) {
    return tf.tidy( () => {
      if ( !newTensorShapeFirst )
        newTensorShapeFirst = newTensorShape;

      let valueCountFirst = tf.util.sizeFromShape( newTensorShape );  // first element (i.e. first stage)
      let valueCount = tf.util.sizeFromShape( newTensorShape );

      let tensor1d, tensorNew;
      let tensorNewArray = new Array( blockCount );
      for ( let i = 0; i < blockCount; ++i ) {
        if ( bNullElement ) {
          if ( 0 == i ) {
            tensor1d = tf.range( 0, valueCountFirst, 1 );
            tensorNew = tensor1d.reshape( newTensorShapeFirst );
          } else {
            tensor1d = tf.range( 0, valueCount, 1 );
            tensorNew = tensor1d.reshape( newTensorShape );
          }
          tensorNewArray[ i ] = tensorNew;
        } else {
          tensorNewArray[ i ] = null;
        }
      }
      return tensorNewArray;
    });
  }

  disposeTensors() {
    if ( this.depthwiseFiltersTensor4dArray ) {
      tf.dispose( this.depthwiseFiltersTensor4dArray );
      this.depthwiseFiltersTensor4dArray = null;
    }

    if ( this.depthwiseBiasesTensor3dArray ) {
      tf.dispose( this.depthwiseBiasesTensor3dArray );
      this.depthwiseBiasesTensor3dArray = null;
    }

    if ( this.pointwiseFiltersTensor4dArray ) {
      tf.dispose( this.pointwiseFiltersTensor4dArray );
      this.pointwiseFiltersTensor4dArray = null;
    }

    if ( this.pointwiseBiasesTensor3dArray ) {
      tf.dispose( this.pointwiseBiasesTensor3dArray );
      this.pointwiseBiasesTensor3dArray = null;
    }
  }

  /**
   * @param  {tf.tensor4d} inputTensor  Apply this tensor with depthwise (bias and activation) and pointwise (bias and activation).
   * @param  {number}      blockIndex   Which block's depthwise and pointwise operrations.
   * @return {tf.tensor4d} Return a new tensor. All other tensors (including inputTensor) were disposed.
   */
  apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( inputTensor, blockIndex ) {
    let t = inputTensor, tNew;

    if ( this.bDepthwiseBias ) {
      tNew = t.add( this.depthwiseBiasesTensor3dArray[ blockIndex ] );
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;
    }

    if ( this.depthwiseActivationFunction ) {
      tNew = this.depthwiseActivationFunction( t );
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;
    }

    if ( this.bPointwise ) {
      tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ blockIndex ], 1, "valid" ); // 1x1, Stride = 1
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;

      if ( this.bPointwiseBias ) {
        tNew = t.add( this.pointwiseBiasesTensor3dArray[ blockIndex ] );
        t.dispose();                                       // Dispose all intermediate (temporary) data.
        t = tNew;
      }

      if ( this.pointwiseActivationFunction ) {
        tNew = this.pointwiseActivationFunction( t );
        t.dispose();                                       // Dispose all intermediate (temporary) data.
        t = tNew;
      }
    }

    return t;
  }

  /**
   * @param sourceImage
   *   The image which will be processed.
   *
   * @param bReturn
   *   If true, the result convoluiotn will be returned.
   *
   * @return
   *   If ( bReturn == true ), return the convolution result (tensor). Otheriwse, reurn null.
   */
  apply( sourceImage, bReturn ) {
    return tf.tidy( () => {

      let t, tNew;

      // Layer 0
      //
      // So that every layer could dispose previous result without worry about mis-dispose source.

      if ( this.bDepthwiseAvg ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
      } else if ( this.bDepthwiseMax ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
      } else if ( this.bDepthwiseConv ) {
        t = sourceImage.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ 0 ], 1, "valid" );  // Stride = 1
      }
      // NOTE: Do not dispose the original data.

      t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( t, 0 );

      // Layer 1, ...
      if ( this.bDepthwiseAvg ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      } else if ( this.bDepthwiseMax ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      } else if ( this.bDepthwiseConv ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ i ], 1, "valid" );  // Stride = 1
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      }

      if ( bReturn )
        return t;
    });
  }

}

<commit_msg>Update TestFilters2D.js<commit_after>
export { Base };

/**
 * One block of testing filters.
 */
class Block {

  init(
    sourceChannelCount,
    filterHeight, channelMultiplier,
    stepCountPerBlock,
    strAvgMaxConv, bDepthwiseBias, depthwiseActivationName,
    bPointwise, bPointwiseBias, pointwiseActivationName ) {

    this.disposeTensors();

    this.stepCountPerBlock = stepCountPerBlock;

    this.filterHeight = filterHeight;
    let filterWidth = filterHeight;

    this.channelMultiplier = channelMultiplier;
    this.channelCount = sourceChannelCount * channelMultiplier;

    this.strAvgMaxConv = strAvgMaxConv;
    switch ( strAvgMaxConv ) {
      case "Avg":  this.bDepthwiseAvg = true;
      case "Max":  this.bDepthwiseMax = true;
      case "Conv": this.bDepthwiseConv = true;
    }

    this.bDepthwiseBias = bDepthwiseBias;

    this.depthwiseActivationName = depthwiseActivationName;
    switch ( depthwiseActivationName ) {
      case "relu":    this.depthwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.depthwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.depthwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.depthwiseActivationFunction = tf.tanh;    break;
      case "sin":     this.depthwiseActivationFunction = tf.sin;     break;
      //default:
    }

    this.bPointwise = bPointwise;
    this.bPointwiseBias = bPointwiseBias;

    this.pointwiseActivationName = pointwiseActivationName;
    switch ( pointwiseActivationName ) {
      case "relu":    this.pointwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.pointwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.pointwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.pointwiseActivationFunction = tf.tanh;    break;
      case "sin":     this.pointwiseActivationFunction = tf.sin;     break;
      //default:
    }

//!!! ...unfinished...
  }

  disposeTensors() {
  }

  /**
   * @param  {tf.tensor4d} inputTensor  Apply this tensor with depthwise (bias and activation) and pointwise (bias and activation).
   * @param  {number}      blockIndex   Which block's depthwise and pointwise operrations.
   * @return {tf.tensor4d} Return a new tensor. All other tensors (including inputTensor) were disposed.
   */
  apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( inputTensor, blockIndex ) {
    let t = inputTensor, tNew;

    if ( this.bDepthwiseBias ) {
      tNew = t.add( this.depthwiseBiasesTensor3dArray[ blockIndex ] );
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;
    }

    if ( this.depthwiseActivationFunction ) {
      tNew = this.depthwiseActivationFunction( t );
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;
    }

    if ( this.bPointwise ) {
      tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ blockIndex ], 1, "valid" ); // 1x1, Stride = 1
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;

      if ( this.bPointwiseBias ) {
        tNew = t.add( this.pointwiseBiasesTensor3dArray[ blockIndex ] );
        t.dispose();                                       // Dispose all intermediate (temporary) data.
        t = tNew;
      }

      if ( this.pointwiseActivationFunction ) {
        tNew = this.pointwiseActivationFunction( t );
        t.dispose();                                       // Dispose all intermediate (temporary) data.
        t = tNew;
      }
    }

    return t;
  }

}

/**
 * Testing Filters of multiple blocks.
 *
 * @member {string} name This test filters' name.
 */
class Base {

  /**
   * @param sourceHeight        The height (and width) of the source image which will be processed by apply().
   * @param sourceChannelCount  The channel count of the source image.
   * @param targetHeight        The taregt image height (and width).
   * @param filterHeight        The height (and width) of each depthwise convolution.
   *
   * @param {number} channelMultiplierBlock0
   *   The first block (Block 0) will expand input channel by this factor.
   *
   * @param {number} stepCountPerBlock
   *   If zero or negative (<= 0), every block will use only one tf.depthwiseConv2d( strides = 1, pad = "valid" ) for shrinking sourceHeight
   * (minus ( filterHeight - 1 )). If positive (>= 1), every block will use one tf.depthwiseConv2d( strides = 2, pad = "same" ) to shrink
   * (half downsample) and use ( stageCountPerBlock - 1 ) times tf.depthwiseConv2d( strides = 1, pad = "same" ) until the block end.
   *
   * @param strAvgMaxConv
   *   Depthwise operation. "Avg" or "Max" or "Conv" for average pooling, max pooling, depthwise convolution.
   *
   * @param bDepthwiseBias
   *   If true, there will be a bias after depthwise convolution.
   *
   * @param depthwiseActivationName
   *   The activation function name after depthwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   *
   * @param bPointwise
   *   If true, there will be pointwise convolution after every layer of depthwise convolution.
   *
   * @param bPointwiseBias
   *   If true, there will be a bias after pointwise convolution. If ( bPointwise == false ), this will be also ignored.
   *
   * @param pointwiseActivationName
   *   The activation function name after pointwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   * If ( bPointwise == false ), this activation function will be also ignored.
   */
  init(
    sourceHeight, sourceChannelCount, targetHeight,
    filterHeight, channelMultiplierBlock0,
    stepCountPerBlock,
    strAvgMaxConv, bDepthwiseBias, depthwiseActivationName,
    bPointwise, bPointwiseBias, pointwiseActivationName ) {

    this.disposeTensors();

    this.stepCountPerBlock = stepCountPerBlock;

    let differenceHeight = sourceHeight - targetHeight;
    let filterWidth = filterHeight;

    this.channelMultiplier = channelMultiplierBlock0;
    this.channelCountBlock0 = sourceChannelCount * channelMultiplierBlock0;  // the channel count of the first block (Block 0).

//!!! ...unfinished...

    // The height of processed image will be reduced a little for any depthwise filter larger than 1x1.
    let heightReducedPerStep = filterHeight - 1;

    if ( stepCountPerBlock >= 1 ) {
      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 2, pad = "same" ).
      this.blockCount = Math.floor( Math.log2( sourceHeight ) );
    } else {
      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 1, pad = "valid" ).
      this.blockCount = Math.floor( differenceHeight / heightReducedPerStep );
    }

//!!! ...unfinished... should use Block0 info (instead of this)

    // e.g. "C24_24__DConv_101x101_DBias_RELU__PConv_PBias_RELU__Block_1__Step_1"
    this.name = `C${sourceChannelCount}_${this.channelCountBlock0}`
      + `__D${strAvgMaxConv}_${filterHeight}x${filterHeight}`
      + `${ ( this.bDepthwiseBias ) ? ( "_DBias" ) : "" }`
      + `${ ( this.depthwiseActivationFunction ) ? ( "_" + depthwiseActivationName ) : "" }`
      + `${ ( this.bPointwise ) ? "__PConv" : "" }`
      + `${ ( this.bPointwise && this.bPointwiseBias ) ? ( "_PBias" ) : "" }`
      + `${ ( this.bPointwise && this.pointwiseActivationFunction ) ? ( "_" + pointwiseActivationName ) : "" }`
      + `__Block_${this.blockCount}`
      + `__Step${stepCountPerBlock}`
    ;

    // Depthwise Filters and Biases

    this.depthwiseFilterHeightWidth = [ filterHeight, filterWidth ];

    // First block's depthwise filters will expand channel count from sourceChannelCount to ( sourceChannelCount * channelMultiplierBlock0 ).
    this.depthwiseFiltersShapeFirst = [ filterHeight, filterWidth,      sourceChannelCount, channelMultiplierBlock0 ];
    this.depthwiseFiltersShape =      [ filterHeight, filterWidth, this.channelCountBlock0,                       1 ];
    this.depthwiseBiasesShape =       [            1,           1, this.channelCountBlock0 ];

    // Every element (Tensor4d) is a depthwiseFilters for one block.
    this.depthwiseFiltersTensor4dArray
      = Base.generateTensorArray( this.blockCount, this.depthwiseFiltersShapeFirst, this.depthwiseFiltersShape, this.bDepthwiseConv );

    // Every element (Tensor3d) is a depthwiseBiases for one block.
    this.depthwiseBiasesTensor3dArray
      = Base.generateTensorArray( this.blockCount, null, this.depthwiseBiasesShape, ( this.bDepthwiseConv && bDepthwiseBias ) );

    // Pointwise Filters and Biases

    let pointwiseInputDepth =  this.channelCountBlock0;
    let pointwiseOutputDepth = this.channelCountBlock0; // Assume output depth is the same as input.

    this.pointwiseFilterHeightWidth = [ 1, 1 ];
    this.pointwiseFiltersShape = [ 1, 1, pointwiseInputDepth, pointwiseOutputDepth ];

    // Both input depth and output depth of pointwise bias are the same as pointwise convolution output.
    this.pointwiseBiasesShape =  [ 1, 1, pointwiseOutputDepth ];

    // Every element (Tensor4d) is a pointwiseFilters for one block.
    this.pointwiseFiltersTensor4dArray
      = Base.generateTensorArray( this.blockCount, null, this.pointwiseFiltersShape, bPointwise );

    // Every element (Tensor3d) is a pointwiseBiases for one block.
    this.pointwiseBiasesTensor3dArray
      = Base.generateTensorArray( this.blockCount, null, this.pointwiseBiasesShape, ( bPointwise && bPointwiseBias ) );
  }

  /**
   * @param {number}   blockCount          The element count (i.e. length) of the returned array.
   * @param {number[]} newTensorShapeFirst The tensor's shape of first element of the returned array. If null, same as newTensorShape.
   * @param {number[]} newTensorShape      The tensor's shape of every (except first) element of the returned array.
   * @param {boolean}  bNullElement        If true, every element of the return array will be null.
   *
   * @return {tf.tensor4d[]|tf.tensor3d[]}
   *   Return a array whose every element is a tensor4d or tensor3d (for one block), or null (if ( bNullElement == true ) ).
   */
  static generateTensorArray( blockCount, newTensorShapeFirst, newTensorShape, bNullElement ) {
    return tf.tidy( () => {
      if ( !newTensorShapeFirst )
        newTensorShapeFirst = newTensorShape;

      let valueCountFirst = tf.util.sizeFromShape( newTensorShape );  // first element (i.e. first stage)
      let valueCount = tf.util.sizeFromShape( newTensorShape );

      let tensor1d, tensorNew;
      let tensorNewArray = new Array( blockCount );
      for ( let i = 0; i < blockCount; ++i ) {
        if ( bNullElement ) {
          if ( 0 == i ) {
            tensor1d = tf.range( 0, valueCountFirst, 1 );
            tensorNew = tensor1d.reshape( newTensorShapeFirst );
          } else {
            tensor1d = tf.range( 0, valueCount, 1 );
            tensorNew = tensor1d.reshape( newTensorShape );
          }
          tensorNewArray[ i ] = tensorNew;
        } else {
          tensorNewArray[ i ] = null;
        }
      }
      return tensorNewArray;
    });
  }

  disposeTensors() {
    if ( this.depthwiseFiltersTensor4dArray ) {
      tf.dispose( this.depthwiseFiltersTensor4dArray );
      this.depthwiseFiltersTensor4dArray = null;
    }

    if ( this.depthwiseBiasesTensor3dArray ) {
      tf.dispose( this.depthwiseBiasesTensor3dArray );
      this.depthwiseBiasesTensor3dArray = null;
    }

    if ( this.pointwiseFiltersTensor4dArray ) {
      tf.dispose( this.pointwiseFiltersTensor4dArray );
      this.pointwiseFiltersTensor4dArray = null;
    }

    if ( this.pointwiseBiasesTensor3dArray ) {
      tf.dispose( this.pointwiseBiasesTensor3dArray );
      this.pointwiseBiasesTensor3dArray = null;
    }
  }

  /**
   * @param sourceImage
   *   The image which will be processed.
   *
   * @param bReturn
   *   If true, the result convoluiotn will be returned.
   *
   * @return
   *   If ( bReturn == true ), return the convolution result (tensor). Otheriwse, reurn null.
   */
  apply( sourceImage, bReturn ) {
    return tf.tidy( () => {

      let t, tNew;

      // Layer 0
      //
      // So that every layer could dispose previous result without worry about mis-dispose source.

      if ( this.bDepthwiseAvg ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
      } else if ( this.bDepthwiseMax ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
      } else if ( this.bDepthwiseConv ) {
        t = sourceImage.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ 0 ], 1, "valid" );  // Stride = 1
      }
      // NOTE: Do not dispose the original data.

      t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( t, 0 );

      // Layer 1, ...
      if ( this.bDepthwiseAvg ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      } else if ( this.bDepthwiseMax ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      } else if ( this.bDepthwiseConv ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ i ], 1, "valid" );  // Stride = 1
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      }

      if ( bReturn )
        return t;
    });
  }

}

<|endoftext|>


<commit_before>'use strict';

var GitHubApi = require('github');
var github = new GitHubApi({
    version: '3.0.0',
    debug: false,
    protocol: 'https',
    host: 'api.github.com',
    timeout: 5000,
    headers: {
        'user-agent': 'webtask-tfa-monitor (https://github.com/radekk/webtask-tfa-monitor/)'
    }
});

/**
 * @param {secret} ORGANIZATION - Github ORGANIZATION name
 * @param {secret} GITHUB_TOKEN - Github API Token with "org:read" permission
 * @return JSON [{username: ''}]
 */
module.exports = function(cx, cb) {
    var ORGANIZATION = cx.secrets.ORGANIZATION;
    var GITHUB_TOKEN = cx.secrets.GITHUB_TOKEN;

    github.authenticate({
        type: 'token',
        token: GITHUB_TOKEN
    });

    // @TODO handle pagination
    github.orgs.getMembers({
        org: ORGANIZATION,
        per_page: 100,
        filter: '2fa_disabled'
    }, function(err, res) {
        if (err) return cb(err);

        if (res && res.length) {
            var members = res.map(data => data.login);

            return cb(null, members);
        }

        return [];
    });
};
<commit_msg>Refactor github monitor.<commit_after>'use strict';

const GitHubApi = require('github');
const github = new GitHubApi({
  version: '3.0.0',
  debug: false,
  protocol: 'https',
  host: 'api.github.com',
  timeout: 5000,
  headers: {
    'user-agent': 'webtask-mfa-monitor (https://github.com/radekk/webtask-mfa-monitor/)'
  }
});

/**
 * @param {secret} ORGANIZATION - Github ORGANIZATION name
 * @param {secret} GITHUB_TOKEN - Github API Token with "org:read" permission
 * @return JSON ['john', 'mark']
 */
module.exports = (ctx, cb) => {
  github.authenticate({
    type: 'token',
    token: ctx.secrets.GITHUB_TOKEN
  });

  github.orgs.getMembers({
    org: ctx.secrets.ORGANIZATION,
    per_page: 100,
    filter: '2fa_disabled'
  }, (err, res) => {
    if (err) return cb(err);
    if (res && res.length) return cb(null, res.map(data => data.login));

    return [];
  });
};
<|endoftext|>


<commit_before>import * as ShuffleNetV2_MobileNetV2_Block from "../Layer/ShuffleNetV2_MobileNetV2_Block.js";

export { Base };

/**
 * Testing Filters of multiple blocks.
 *
 * @member {string} name
 *   This test filters' name.
 *
 * @member {number[]} sourceImageHeightWidth
 *   The size (i.e. [ height, width ]) of the source image. When apply() is called, the source image will be extracted from the sourceCanvas
 * and be resized to this size. The neural network receives this resized source image.
 */
class Base {

  /**
   * @param {number} depthwiseChannelMultiplierBlock0Step0
   *   The depthwise convolution of the first step (Step 0) of the first block (Block 0) will expand input channel by this factor.
   *
   * @see ShuffleNetV2_MobileNetV2_Block.init
   */
  init(
    sourceHeight, sourceChannelCount, targetHeight,
    stepCountPerBlock,
    bShuffleNetV2,
    strAvgMaxConv, depthwiseFilterHeight, depthwiseChannelMultiplierBlock0Step0, bBias, strActivationName ) {

    this.disposeTensors();

    this.stepCountPerBlock = stepCountPerBlock;
    this.bShuffleNetV2 = bShuffleNetV2;

    let differenceHeight = sourceHeight - targetHeight;
    let filterWidth = depthwiseFilterHeight;

    this.depthwiseChannelMultiplierBlock0Step0 = depthwiseChannelMultiplierBlock0Step0;
    this.channelCountBlock0 = sourceChannelCount * depthwiseChannelMultiplierBlock0Step0;  // the channel count of the first block (Block 0).

    if ( stepCountPerBlock <= 0 ) { // Not ShuffleNetV2, Not MobileNetV2.

      // The height of processed image will be reduced a little for any depthwise filter larger than 1x1.
      let heightReducedPerBlock = depthwiseFilterHeight - 1;

      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 1, pad = "valid" ).
      this.blockCount = Math.floor( differenceHeight / heightReducedPerBlock );

    } else {  // ShuffleNetV2 or MobileNetV2. Halven per block.

      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 2, pad = "same" ).
      this.blockCount = Math.floor( Math.log2( sourceHeight ) );
    }

    this.blocks = new Array( this.blockCount );
    for ( let i = 0; i < this.blockCount; ++i )
    {
      let block = new ShuffleNetV2_MobileNetV2_Block.Base();
      block.init(
        sourceHeight, sourceChannelCount, targetHeight,
        stepCountPerBlock,
        bShuffleNetV2,
        strAvgMaxConv, depthwiseFilterHeight, depthwiseChannelMultiplierBlock0Step0, bBias, strActivationName );
      this.blocks[ i ] = block;
    }

    let block0 = this.blocks[ 0 ];

    // e.g. "C24_24__DConv_101x101_DBias_RELU__PConv_PBias_RELU__Block_1__Step_1"
    this.name = `C${sourceChannelCount}_${this.channelCountBlock0}`
      + `__D${strAvgMaxConv}_${depthwiseFilterHeight}x${depthwiseFilterHeight}`
      + `${ ( block0.step0.bDepthwiseBias ) ? ( "_DBias" ) : "" }`
      + `${ ( block0.step0.depthwiseActivationFunction ) ? ( "_" + strAvgMaxConv ) : "" }`
      + `${ ( block0.step0.bPointwise2 ) ? "__PConv" : "" }`
      + `${ ( block0.step0.bPointwise2 && block0.step0.bPointwise2Bias ) ? ( "_PBias" ) : "" }`
      + `${ ( block0.step0.bPointwise2 && block0.pointwise2ActivationName ) ? ( "_" + block0.step0.pointwise2ActivationName ) : "" }`
      + `__Block_${this.blockCount}`
      + `__Step${stepCountPerBlock}`
    ;

    this.sourceImageHeightWidth = [ block0.sourceHeight, block0.sourceWidth ];
  }

  disposeTensors() {
    if ( this.blocks ) {
      for ( let block of this.blocks ) {
        block.disposeTensors();
      }
      this.blocks = null;
    }
  }

  /**
   * @param {HTMLCanvasElement} sourceCanvas
   *   The canvas which provides image.
   *
   * @param bReturn
   *   If true, the result tensor will be returned. Otherwise, all tensors are disposed.
   *
   * @return
   *   If ( bReturn == true ), return the result tensor. Otheriwse, return null.
   */
  apply( sourceCanvas, bReturn ) {
//    return tf.tidy( () => {

      // Using fromPixels() to get source image so that we can always dispose all tensors (including sourceImage) except the returning tensor.
      let sourceImageChannelCount = 4;
      let sourceImage = tf.browser.fromPixels( sourceCanvas, sourceImageChannelCount );

      // Resize source image to a default size (height x width) which is used when training the neural network.
      let t = tf.image.resizeBilinear( sourceImage, this.sourceImageHeightWidth, true ); // alignCorners = true
      sourceImage.dispose();

      let block;
      for ( let i = 0; i < this.blocks.length; ++i ) {
        block = this.blocks[ i ];
        t = block.apply_and_destroy( t );
      }

      if ( bReturn )
        return t;
      else
        t.dispose();

//    });
  }

}

<commit_msg>Update TestFilters2D.js<commit_after>import * as ShuffleNetV2_MobileNetV2_Block from "../Layer/ShuffleNetV2_MobileNetV2_Block.js";

export { Base };

/**
 * Testing Filters of multiple blocks.
 *
 * @member {string} name
 *   This test filters' name.
 *
 * @member {number[]} sourceImageHeightWidth
 *   The size (i.e. [ height, width ]) of the source image. When apply() is called, the source image will be extracted from the sourceCanvas
 * and be resized to this size. The neural network receives this resized source image.
 */
class Base {

  /**
   * @param {number} depthwiseChannelMultiplierBlock0Step0
   *   The depthwise convolution of the first step (Step 0) of the first block (Block 0) will expand input channel by this factor.
   *
   * @see ShuffleNetV2_MobileNetV2_Block.init
   */
  init(
    sourceHeight, sourceChannelCount, targetHeight,
    stepCountPerBlock,
    bShuffleNetV2,
    strAvgMaxConv, depthwiseFilterHeight, depthwiseChannelMultiplierBlock0Step0, bBias, strActivationName ) {

    this.disposeTensors();

    this.stepCountPerBlock = stepCountPerBlock;
    this.bShuffleNetV2 = bShuffleNetV2;

    let differenceHeight = sourceHeight - targetHeight;
    let filterWidth = depthwiseFilterHeight;

    this.depthwiseChannelMultiplierBlock0Step0 = depthwiseChannelMultiplierBlock0Step0;
    this.channelCountBlock0 = sourceChannelCount * depthwiseChannelMultiplierBlock0Step0;  // the channel count of the first block (Block 0).

    if ( stepCountPerBlock <= 0 ) { // Not ShuffleNetV2, Not MobileNetV2.

      // The height of processed image will be reduced a little for any depthwise filter larger than 1x1.
      let heightReducedPerBlock = depthwiseFilterHeight - 1;

      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 1, pad = "valid" ).
      this.blockCount = Math.floor( differenceHeight / heightReducedPerBlock );

    } else {  // ShuffleNetV2 or MobileNetV2. Halven per block.

      // The block count for reducing sourceHeight to targetHeight by tf.depthwiseConv2d( strides = 2, pad = "same" ).
      this.blockCount = Math.floor( Math.log2( sourceHeight ) );
    }

    this.blocks = new Array( this.blockCount );
    for ( let i = 0; i < this.blockCount; ++i )
    {
      let block = new ShuffleNetV2_MobileNetV2_Block.Base();
      block.init(
        sourceHeight, sourceChannelCount, targetHeight,
        stepCountPerBlock,
        bShuffleNetV2,
        strAvgMaxConv, depthwiseFilterHeight, depthwiseChannelMultiplierBlock0Step0, bBias, strActivationName );
      this.blocks[ i ] = block;
    }

    let block0 = this.blocks[ 0 ];

    // e.g. "C24_24__DConv_101x101_DBias_RELU__PConv_PBias_RELU__Block_1__Step_1"
    this.name = `C${sourceChannelCount}_${this.channelCountBlock0}`
      + `__D${strAvgMaxConv}_${depthwiseFilterHeight}x${depthwiseFilterHeight}`
      + `${ ( block0.step0.bDepthwiseBias ) ? ( "_DBias" ) : "" }`
      + `${ ( block0.step0.depthwiseActivationFunction ) ? ( "_" + strAvgMaxConv ) : "" }`
      + `${ ( block0.step0.bPointwise2 ) ? "__PConv" : "" }`
      + `${ ( block0.step0.bPointwise2 && block0.step0.bPointwise2Bias ) ? ( "_PBias" ) : "" }`
      + `${ ( block0.step0.bPointwise2 && block0.pointwise2ActivationName ) ? ( "_" + block0.step0.pointwise2ActivationName ) : "" }`
      + `__Block_${this.blockCount}`
      + `__Step_${stepCountPerBlock}`
    ;

    this.sourceImageHeightWidth = [ block0.sourceHeight, block0.sourceWidth ];
  }

  disposeTensors() {
    if ( this.blocks ) {
      for ( let block of this.blocks ) {
        block.disposeTensors();
      }
      this.blocks = null;
    }
  }

  /**
   * @param {HTMLCanvasElement} sourceCanvas
   *   The canvas which provides image.
   *
   * @param bReturn
   *   If true, the result tensor will be returned. Otherwise, all tensors are disposed.
   *
   * @return
   *   If ( bReturn == true ), return the result tensor. Otheriwse, return null.
   */
  apply( sourceCanvas, bReturn ) {
//    return tf.tidy( () => {

      // Using fromPixels() to get source image so that we can always dispose all tensors (including sourceImage) except the returning tensor.
      let sourceImageChannelCount = 4;
      let sourceImage = tf.browser.fromPixels( sourceCanvas, sourceImageChannelCount );

      // Resize source image to a default size (height x width) which is used when training the neural network.
      let t = tf.image.resizeBilinear( sourceImage, this.sourceImageHeightWidth, true ); // alignCorners = true
      sourceImage.dispose();

      let block;
      for ( let i = 0; i < this.blocks.length; ++i ) {
        block = this.blocks[ i ];
        t = block.apply_and_destroy( t );
      }

      if ( bReturn )
        return t;
      else
        t.dispose();

//    });
  }

}

<|endoftext|>


<commit_before>const KEEP_ALIVE_DELAY = 60000 // in ms

module.exports = () => {
  const net = require('net')
  const tls = require('tls')

  return ({ host, port, ssl, onConnect }) => {
    const socket = ssl
      ? tls.connect(Object.assign({ host, port }, ssl), onConnect)
      : net.connect({ host, port }, onConnect)

    socket.setKeepAlive(true, KEEP_ALIVE_DELAY)

    return socket
  }
}
<commit_msg>Automatically assign the server name when creating TLS sockets<commit_after>const KEEP_ALIVE_DELAY = 60000 // in ms

module.exports = () => {
  const net = require('net')
  const tls = require('tls')

  return ({ host, port, ssl, onConnect }) => {
    const socket = ssl
      ? tls.connect(Object.assign({ host, port, servername: host }, ssl), onConnect)
      : net.connect({ host, port }, onConnect)

    socket.setKeepAlive(true, KEEP_ALIVE_DELAY)

    return socket
  }
}
<|endoftext|>


<commit_before>JSIL.ConcatString = function (/* ...values */) {
  var result = "";

  if (arguments[0] !== null)
    result = String(arguments[0]);

  for (var i = 1, l = arguments.length; i < l; i++) {
    var arg = arguments[i];
    if (arg === null)
      ;
    else if (typeof (arg) === "string")
      result += arg;
    else if ($jsilcore.System.Char.$Is(arg))
      result += String.fromCharCode(arg.valueOf());
    else
      result += String(arg);
  }

  return result;
};<commit_msg>JSIL.ConcatString fixed.<commit_after>JSIL.ConcatString = function (/* ...values */) {
  var result = "";

  if (arguments[0] !== null)
    result = String(arguments[0]);

  for (var i = 1, l = arguments.length; i < l; i++) {
    var arg = arguments[i];
    if (arg === null)
      ;
    else if (typeof (arg) === "string")
      result += arg;
    else if ($jsilcore.JSIL.Box.IsBoxedOfType(arg, $jsilcore.System.Char))
      result += String.fromCharCode(arg.valueOf());
    else
      result += String(arg);
  }

  return result;
};<|endoftext|>


<commit_before>import {
  React, PropTypes, styled,
} from 'vendor';
import { StepCard } from './card';
import { get } from 'lodash';
import { titleize } from '../../../helpers/object';
import Raven from '../../../models/app/raven';
import Step from '../../../models/student-tasks/step';
import Task from '../../../models/student-tasks/task';
import SupportEmailLink from '../../../components/support-email-link';
import ReloadPageButton from '../../../components/buttons/reload-page';

const StyledFailure = styled(StepCard)`
  p { line-height: 1.5rem; }
  h4 { margin: 2rem 0; }
  button.reload-page { margin: auto; }
`;

class Failure extends React.Component {

  static propTypes = {
    task: PropTypes.instanceOf(Task).isRequired,
    step: PropTypes.instanceOf(Step),
  }

  componentDidMount() {
    const { task, step } = this.props;

    let errMsg = [];
    if (task.api.hasErrors) {
      const { last: _, ...errors } = task.api.errors;
      errMsg.push(`Failed to load assignment, errors: ${titleize(errors)}`);
    }
    // step may not be present if the task had errors
    if (!step) { return; }

    const { last: _, ...errors } = step.api.errors;
    errMsg.push(`Failed to ${this.requestAction} assignment step, errors: ${titleize(errors)}`);
    if (errMsg.length) {
      Raven.log(errMsg.join('\n'), {
        taskId: task.id,
        stepId: step.id,
      });
    }
  }

  get requestAction() {
    return 'get' === get(this.props.step, 'api.errors.last.config.method') ? 'load' : 'save';
  }

  render() {
    return (

    );
  }

}

export default Failure;
<commit_msg>correct mispelled saving<commit_after>import {
  React, PropTypes, styled,
} from 'vendor';
import { StepCard } from './card';
import { get } from 'lodash';
import { titleize } from '../../../helpers/object';
import Raven from '../../../models/app/raven';
import Step from '../../../models/student-tasks/step';
import Task from '../../../models/student-tasks/task';
import SupportEmailLink from '../../../components/support-email-link';
import ReloadPageButton from '../../../components/buttons/reload-page';

const StyledFailure = styled(StepCard)`
  p { line-height: 1.5rem; }
  h4 { margin: 2rem 0; }
  button.reload-page { margin: auto; }
`;

class Failure extends React.Component {

  static propTypes = {
    task: PropTypes.instanceOf(Task).isRequired,
    step: PropTypes.instanceOf(Step),
  }

  componentDidMount() {
    const { task, step } = this.props;

    let errMsg = [];
    if (task.api.hasErrors) {
      const { last: _, ...errors } = task.api.errors;
      errMsg.push(`Failed to load assignment, errors: ${titleize(errors)}`);
    }
    // step may not be present if the task had errors
    if (!step) { return; }

    const { last: _, ...errors } = step.api.errors;
    errMsg.push(`Failed to ${this.isLoading ? 'load' : 'save'} assignment step, errors: ${titleize(errors)}`);
    if (errMsg.length) {
      Raven.log(errMsg.join('\n'), {
        taskId: task.id,
        stepId: step.id,
      });
    }
  }

  get isLoading() {
    return 'get' === get(this.props.step, 'api.errors.last.config.method');
  }

  render() {
    return (

    );
  }

}

export default Failure;
<|endoftext|>


<commit_before>import { ActionsObservable } from 'redux-observable';
import { writeFileObservable } from '../../utils/fs';
import {
  SAVE,
  SAVE_AS,
  CHANGE_FILENAME,
  DONE_SAVING,
} from '../constants';

import {
  changeFilename,
  save,
  saveAs,
  doneSaving,
} from '../actions';

const Rx = require('rxjs/Rx');
const commutable = require('commutable');

const Observable = Rx.Observable;

export function saveEpic(action$) {
  return action$.ofType(SAVE)
    .do(action => {
      // If there isn't a filename, save-as it instead
      if (!action.filename) {
        throw new Error('save needs a filename');
      }
    })
    .mergeMap(action =>
      writeFileObservable(action.filename,
        JSON.stringify(
          commutable.toJS(
            action.notebook.update('cellMap', (cells) =>
              cells.map((value) =>
                value.delete('inputHidden').delete('outputHidden').delete('status')))),
          null,
          1))
        .catch(error => {
          const input$ = Observable.of(error);
          return new ActionsObservable(input$);
        })
        .map(doneSaving)
        // .startWith({ type: START_SAVING })
        // since SAVE effectively acts as the same as START_SAVING
        // you could just look for that in your reducers instead of START_SAVING
    );
}

export function saveAsEpic(actions) {
  return actions.ofType(SAVE_AS)
    .mergeMap(action => [
      changeFilename(action.filename),
      save(action.filename, action.notebook),
    ]);
}
<commit_msg>chore(SavingEpic): Cleanup error observable.<commit_after>import { ActionsObservable } from 'redux-observable';
import { writeFileObservable } from '../../utils/fs';
import {
  SAVE,
  SAVE_AS,
  CHANGE_FILENAME,
  DONE_SAVING,
} from '../constants';

import {
  changeFilename,
  save,
  saveAs,
  doneSaving,
} from '../actions';

const Rx = require('rxjs/Rx');
const commutable = require('commutable');

const Observable = Rx.Observable;

export function saveEpic(action$) {
  return action$.ofType(SAVE)
    .do(action => {
      // If there isn't a filename, save-as it instead
      if (!action.filename) {
        throw new Error('save needs a filename');
      }
    })
    .mergeMap(action =>
      writeFileObservable(action.filename,
        JSON.stringify(
          commutable.toJS(
            action.notebook.update('cellMap', (cells) =>
              cells.map((value) =>
                value.delete('inputHidden').delete('outputHidden').delete('status')))),
          null,
          1))
        .catch(error => {
          const input$ = Observable.of({
            type: 'ERROR',
            payload: error,
            error: true,
          });
          return new ActionsObservable(input$);
        })
        .map(doneSaving)
        // .startWith({ type: START_SAVING })
        // since SAVE effectively acts as the same as START_SAVING
        // you could just look for that in your reducers instead of START_SAVING
    );
}

export function saveAsEpic(actions) {
  return actions.ofType(SAVE_AS)
    .mergeMap(action => [
      changeFilename(action.filename),
      save(action.filename, action.notebook),
    ]);
}
<|endoftext|>