Remove dead code

Remove dead code

<template>
  <v-expansion-panel>
    <v-expansion-panel-header><h3 class="grey--text text--darken-1">
      {{ period.description }}
    </h3></v-expansion-panel-header>
    <story-day v-for="day in period.days().items" :key="day._meta.self"
               :day="day"
               :editing="editing" />
  </v-expansion-panel>
</template>
<script>
import StoryDay from '@/components/camp/StoryDay'

export default {
  name: 'StoryPeriod',
  components: { StoryDay },
  props: {
    period: { type: Object, required: true },
    editing: { type: Boolean, default: false }
  }
}
</script>

<template>
  <v-expansion-panel v-model="expanded">
    <v-expansion-panel-header><h3 class="grey--text text--darken-1">
      {{ period.description }}
    </h3></v-expansion-panel-header>
    <story-day v-for="day in period.days().items" :key="day._meta.self"
               :day="day"
               :editing="editing" />
  </v-expansion-panel>
</template>
<script>
import StoryDay from '@/components/camp/StoryDay'

export default {
  name: 'StoryPeriod',
  components: { StoryDay },
  props: {
    period: { type: Object, required: true },
    editing: { type: Boolean, default: false }
  },
  data () {
    return {
      // Collapse if the period is in the past
      expanded: Date.parse(this.period.end) >= new Date()
    }
  }
}
</script>


add params to banner links after banner show was set to true

remp/remp#455

<style type="text/css">
    @import '../../sass/banner.scss';
</style>

<template>
    <div class="remp-banner" :id="wrapperId">
        <html-preview v-if="template === 'html'"
                :alignmentOptions="alignmentOptions"
                :dimensionOptions="dimensionOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :textAlign="htmlTemplate.textAlign"
                :dimensions="htmlTemplate.dimensions"
                :textColor="htmlTemplate.textColor"
                :fontSize="htmlTemplate.fontSize"
                :backgroundColor="htmlTemplate.backgroundColor"
                :text="htmlTemplate.text"
                :css="htmlTemplate.css"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></html-preview>

        <medium-rectangle-preview v-if="template === 'medium_rectangle'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :headerText="mediumRectangleTemplate.headerText"
                :mainText="mediumRectangleTemplate.mainText"
                :buttonText="mediumRectangleTemplate.buttonText"
                :width="mediumRectangleTemplate.width"
                :height="mediumRectangleTemplate.height"
                :backgroundColor="mediumRectangleTemplate.backgroundColor"
                :textColor="mediumRectangleTemplate.textColor"
                :buttonBackgroundColor="mediumRectangleTemplate.buttonBackgroundColor"
                :buttonTextColor="mediumRectangleTemplate.buttonTextColor"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></medium-rectangle-preview>

        <bar-preview v-if="template === 'bar'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :mainText="barTemplate.mainText"
                :buttonText="barTemplate.buttonText"
                :backgroundColor="barTemplate.backgroundColor"
                :textColor="barTemplate.textColor"
                :buttonBackgroundColor="barTemplate.buttonBackgroundColor"
                :buttonTextColor="barTemplate.buttonTextColor"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></bar-preview>

        <collapsible-bar-preview v-if="template === 'collapsible_bar'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :mainText="collapsibleBarTemplate.mainText"
                :headerText="collapsibleBarTemplate.headerText"
                :collapseText="collapsibleBarTemplate.collapseText"
                :expandText="collapsibleBarTemplate.expandText"
                :buttonText="collapsibleBarTemplate.buttonText"
                :backgroundColor="collapsibleBarTemplate.backgroundColor"
                :textColor="collapsibleBarTemplate.textColor"
                :buttonBackgroundColor="collapsibleBarTemplate.buttonBackgroundColor"
                :buttonTextColor="collapsibleBarTemplate.buttonTextColor"
                :initialState="collapsibleBarTemplate.initialState"

                :targetUrl="targetUrl"
                :transition="transition"
                :displayType="displayType"
        ></collapsible-bar-preview>

        <short-message-preview v-if="template === 'short_message'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :text="shortMessageTemplate.text"
                :backgroundColor="shortMessageTemplate.backgroundColor"
                :textColor="shortMessageTemplate.textColor"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></short-message-preview>

        <overlay-rectangle-preview v-if="template === 'overlay_rectangle'"
                :alignmentOptions="alignmentOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"

                :headerText="overlayRectangleTemplate.headerText"
                :mainText="overlayRectangleTemplate.mainText"
                :buttonText="overlayRectangleTemplate.buttonText"
                :width="overlayRectangleTemplate.width"
                :height="overlayRectangleTemplate.height"
                :backgroundColor="overlayRectangleTemplate.backgroundColor"
                :textColor="overlayRectangleTemplate.textColor"
                :buttonBackgroundColor="overlayRectangleTemplate.buttonBackgroundColor"
                :buttonTextColor="overlayRectangleTemplate.buttonTextColor"
                :imageLink="overlayRectangleTemplate.imageLink"

                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
                :adminPreview="adminPreview"
        >
        </overlay-rectangle-preview>

        <html-overlay-preview v-if="template === 'html_overlay'"
              :alignmentOptions="alignmentOptions"
              :show="visible"
              :uuid="uuid"
              :campaignUuid="campaignUuid"

              :textAlign="htmlOverlayTemplate.textAlign"
              :textColor="htmlOverlayTemplate.textColor"
              :fontSize="htmlOverlayTemplate.fontSize"
              :backgroundColor="htmlOverlayTemplate.backgroundColor"
              :text="htmlOverlayTemplate.text"
              :css="htmlOverlayTemplate.css"

              :targetUrl="targetUrl"
              :closeable="closeable"
              :closeText="closeText"
              :transition="transition"
              :displayType="displayType"
        ></html-overlay-preview>
    </div>
</template>

<script>
    import HtmlPreview from "./previews/Html";
    import MediumRectanglePreview from "./previews/MediumRectangle";
    import BarPreview from "./previews/Bar";
    import CollapsibleBarPreview from "./previews/CollapsibleBar";
    import ShortMessagePreview from "./previews/ShortMessage";
    import OverlayRectanglePreview from "./previews/OverlayRectangle";
    import HtmlOverlayPreview from "./previews/HtmlOverlay";

    import lib from "remp/js/remplib.js";

    const props = [
        "name",
        "targetUrl",
        "position",
        "offsetVertical",
        "offsetHorizontal",
        "transition",
        "closeable",
        "closeText",
        "displayDelay",
        "closeTimeout",
        "targetSelector",
        "displayType",
        "template",
        "uuid",
        "campaignUuid",
        "forcedPosition",
        "show",

        "variables",
        "urlParams",

        "mediumRectangleTemplate",
        "barTemplate",
        "collapsibleBarTemplate",
        "htmlTemplate",
        "shortMessageTemplate",
        "overlayRectangleTemplate",
        "htmlOverlayTemplate",

        "alignmentOptions",
        "dimensionOptions",
        "positionOptions",

        "variantUuid",

        "adminPreview",

        "js",
        "jsIncludes",
        "cssIncludes",
    ];

    export default {
        components: {
            HtmlPreview,
            MediumRectanglePreview,
            BarPreview,
            CollapsibleBarPreview,
            ShortMessagePreview,
            OverlayRectanglePreview,
            HtmlOverlayPreview,
        },
        name: 'banner-preview',
        props: props,
        created: function() {
            this.$on('values-changed', function(data) {
                for (let item of data) {
                    this[item.key] = item.val;
                }
            });
        },
        mounted: function() {
            this.wrapperId = 'remp-banner-' + lib.uuidv4();

            props.forEach((prop) => {
                this[prop.slice(1)] = this[prop];
            });

            this.visible = this.show;

            if (this.cssIncludes) {
                for (let ii = 0; ii < this.cssIncludes.length; ii++) {
                    lib.loadStyle(this.cssIncludes[ii]);
                }
            }

            let vm = this,
                js = this.js,
                loadedScriptsCount = 0;

            if (this.jsIncludes) {
                for (let ii = 0; ii < this.jsIncludes.length; ii++) {
                    if (!this.jsIncludes[ii]) {
                        loadedScriptsCount++;
                        continue;
                    }

                    lib.loadScript(this.jsIncludes[ii], function () {
                        loadedScriptsCount++;
                        if (loadedScriptsCount === vm.jsIncludes.length) {
                            vm.runCustomJavascript(js);
                        }
                    });
                }
            } else {
                this.runCustomJavascript(js);
            }
        },
        data: () => ({
            visible: false,
            wrapperId: null,
            paramsAdded: false
        }),
        watch: {
            'transition': function() {
                let vm = this;
                setTimeout(function() { vm.visible = false }, 100);
                setTimeout(function() { vm.visible = true }, 800);
            },
            'show': {
                handler: function() {
                    if (this.show) {
                        this.addParamsToLinks();
                    }

                    this.visible = this.show;
                },
                immediate: true
            }
        },
        computed: {
            url: function() {
                if (this.targetUrl) {
                    return this.targetUrl;
                }
                return null;
            },
        },
        methods: {
            addParamsToLinks: function () {
                if (!this.paramsAdded) {
                    this.$nextTick(() => {
                        let wrap = document.getElementById(this.wrapperId);
                        let hrefs = wrap.getElementsByTagName('a');

                        for(let ii = 0; ii < hrefs.length; ii++) {
                            let href = hrefs[ii].getAttribute('href'),
                                linkParams = {};

                            [].forEach.call(hrefs[ii].attributes, function(attr) {
                                if (/^data-param-/.test(attr.name)) {
                                    let camelCaseName = attr.name.substr(11).replace(/-(.)/g, function ($0, $1) {
                                        return $1.toUpperCase();
                                    });
                                    linkParams[camelCaseName] = attr.value;
                                }
                            });

                            if (href) {
                                hrefs[ii].setAttribute('href', this.addUrlParams(href, linkParams));
                            }
                        }
                    }, this);
                }

                this.paramsAdded = true;
            },
            addUrlParams: function(url, linkParams) {
                let separator = url.indexOf("?") === -1 ? "?" : "&";
                url =  url + separator + "utm_source=remp_campaign" +
                    "&utm_medium=" + encodeURIComponent(this.displayType);
                if (this.campaignUuid) {
                    url += "&utm_campaign=" + encodeURIComponent(this.campaignUuid);
                }
                if (this.uuid) {
                    url += "&utm_content=" + encodeURIComponent(this.uuid);
                }
                if (this.variantUuid) {
                    url += "&banner_variant=" + encodeURIComponent(this.variantUuid);
                }

                if (this.urlParams) {
                    for (let param in this.urlParams) {
                        if (this.urlParams.hasOwnProperty(param)) {
                            url += "&" + encodeURIComponent(param) + '=' + encodeURIComponent(this.urlParams[param]())
                        }
                    }
                }

                if (linkParams) {
                    for (let param in linkParams) {
                        if (linkParams.hasOwnProperty(param)) {
                            url += "&" + encodeURIComponent(param) + '=' + encodeURIComponent(linkParams[param])
                        }
                    }
                }

                return url;
            },
            injectVars: function(str) {
                if (!remplib || !remplib.campaign) {
                    return str;
                }
                let re = /\{\{\s?(.*?)\s?\}\}/g;
                let match;

                while (match = re.exec(str)) {
                    let replRegex = new RegExp(match[0], "g");
                    let replVal = '';
                    if (remplib.campaign.variables && remplib.campaign.variables.hasOwnProperty(match[1])) {
                        replVal = remplib.campaign.variables[match[1]].value()
                    } else {
                        throw EvalError("cannot render banner, variable [" + match[1] + "] is missing");
                    }
                    str = str.replace(replRegex, replVal);
                }
                return str;
            },
            closed: function() {
				this.visible = false;
				this.$parent.$emit('values-changed', [
                    {key: "show", val: false}
                ]);
                if (this.closeTracked) {
                    return true;
                }
                this.trackEvent("banner", "close", null, null, {
                    "utm_source": "remp_campaign",
                    "utm_medium": this.displayType,
                    "utm_campaign": this.campaignUuid,
                    "utm_content": this.uuid,
                    "banner_variant": this.variantUuid
                });
                this.closeTracked = true;
            },
            clicked: function(event, hideBanner = false) {
                if (this.clickTracked) {
                    return true;
                }
                this.trackEvent("banner", "click", null, null, {
                    "utm_source": "remp_campaign",
                    "utm_medium": this.displayType,
                    "utm_campaign": this.campaignUuid,
                    "utm_content": this.uuid,
                    "banner_variant": this.variantUuid
                });
                this.clickTracked = true;
                if (hideBanner) {
                    this.visible = false;
                }
                return true;
            },
            trackEvent: function(category, action, tags, fields, source) {
                if (typeof remplib.tracker === 'undefined') {
                    return;
                }
                remplib.tracker.trackEvent(category, action, tags, fields, source);
            },
            customPositioned: function() {
                if (this.displayType === 'inline') {
                    return false;
                }
                if (this.displayType === 'overlay') {
                    return true;
                }
                if (this.forcedPosition !== undefined && this.forcedPosition === 'absolute') {
                    return true;
                }
                return false;
            },
            runCustomJavascript: function (js) {
                this.$nextTick(() => {
                    setTimeout(function() {
                        try {
                            eval('(function() {' + js + '})()');
                        } catch {
                            console.warn("unable to execute custom banner JS:", js);
                        }
                    }, 0);
                }, this)
            }
        }
    }
</script>

<style type="text/css">
    @import '../../sass/banner.scss';
</style>

<template>
    <div class="remp-banner" :id="wrapperId">
        <html-preview v-if="template === 'html'"
                :alignmentOptions="alignmentOptions"
                :dimensionOptions="dimensionOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :textAlign="htmlTemplate.textAlign"
                :dimensions="htmlTemplate.dimensions"
                :textColor="htmlTemplate.textColor"
                :fontSize="htmlTemplate.fontSize"
                :backgroundColor="htmlTemplate.backgroundColor"
                :text="htmlTemplate.text"
                :css="htmlTemplate.css"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></html-preview>

        <medium-rectangle-preview v-if="template === 'medium_rectangle'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :headerText="mediumRectangleTemplate.headerText"
                :mainText="mediumRectangleTemplate.mainText"
                :buttonText="mediumRectangleTemplate.buttonText"
                :width="mediumRectangleTemplate.width"
                :height="mediumRectangleTemplate.height"
                :backgroundColor="mediumRectangleTemplate.backgroundColor"
                :textColor="mediumRectangleTemplate.textColor"
                :buttonBackgroundColor="mediumRectangleTemplate.buttonBackgroundColor"
                :buttonTextColor="mediumRectangleTemplate.buttonTextColor"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></medium-rectangle-preview>

        <bar-preview v-if="template === 'bar'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :mainText="barTemplate.mainText"
                :buttonText="barTemplate.buttonText"
                :backgroundColor="barTemplate.backgroundColor"
                :textColor="barTemplate.textColor"
                :buttonBackgroundColor="barTemplate.buttonBackgroundColor"
                :buttonTextColor="barTemplate.buttonTextColor"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></bar-preview>

        <collapsible-bar-preview v-if="template === 'collapsible_bar'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :mainText="collapsibleBarTemplate.mainText"
                :headerText="collapsibleBarTemplate.headerText"
                :collapseText="collapsibleBarTemplate.collapseText"
                :expandText="collapsibleBarTemplate.expandText"
                :buttonText="collapsibleBarTemplate.buttonText"
                :backgroundColor="collapsibleBarTemplate.backgroundColor"
                :textColor="collapsibleBarTemplate.textColor"
                :buttonBackgroundColor="collapsibleBarTemplate.buttonBackgroundColor"
                :buttonTextColor="collapsibleBarTemplate.buttonTextColor"
                :initialState="collapsibleBarTemplate.initialState"

                :targetUrl="targetUrl"
                :transition="transition"
                :displayType="displayType"
        ></collapsible-bar-preview>

        <short-message-preview v-if="template === 'short_message'"
                :alignmentOptions="alignmentOptions"
                :positionOptions="positionOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"
                :forcedPosition="forcedPosition"

                :text="shortMessageTemplate.text"
                :backgroundColor="shortMessageTemplate.backgroundColor"
                :textColor="shortMessageTemplate.textColor"

                :position="position"
                :offsetVertical="offsetVertical"
                :offsetHorizontal="offsetHorizontal"
                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
        ></short-message-preview>

        <overlay-rectangle-preview v-if="template === 'overlay_rectangle'"
                :alignmentOptions="alignmentOptions"
                :show="visible"
                :uuid="uuid"
                :campaignUuid="campaignUuid"

                :headerText="overlayRectangleTemplate.headerText"
                :mainText="overlayRectangleTemplate.mainText"
                :buttonText="overlayRectangleTemplate.buttonText"
                :width="overlayRectangleTemplate.width"
                :height="overlayRectangleTemplate.height"
                :backgroundColor="overlayRectangleTemplate.backgroundColor"
                :textColor="overlayRectangleTemplate.textColor"
                :buttonBackgroundColor="overlayRectangleTemplate.buttonBackgroundColor"
                :buttonTextColor="overlayRectangleTemplate.buttonTextColor"
                :imageLink="overlayRectangleTemplate.imageLink"

                :targetUrl="targetUrl"
                :closeable="closeable"
                :closeText="closeText"
                :transition="transition"
                :displayType="displayType"
                :adminPreview="adminPreview"
        >
        </overlay-rectangle-preview>

        <html-overlay-preview v-if="template === 'html_overlay'"
              :alignmentOptions="alignmentOptions"
              :show="visible"
              :uuid="uuid"
              :campaignUuid="campaignUuid"

              :textAlign="htmlOverlayTemplate.textAlign"
              :textColor="htmlOverlayTemplate.textColor"
              :fontSize="htmlOverlayTemplate.fontSize"
              :backgroundColor="htmlOverlayTemplate.backgroundColor"
              :text="htmlOverlayTemplate.text"
              :css="htmlOverlayTemplate.css"

              :targetUrl="targetUrl"
              :closeable="closeable"
              :closeText="closeText"
              :transition="transition"
              :displayType="displayType"
        ></html-overlay-preview>
    </div>
</template>

<script>
    import HtmlPreview from "./previews/Html";
    import MediumRectanglePreview from "./previews/MediumRectangle";
    import BarPreview from "./previews/Bar";
    import CollapsibleBarPreview from "./previews/CollapsibleBar";
    import ShortMessagePreview from "./previews/ShortMessage";
    import OverlayRectanglePreview from "./previews/OverlayRectangle";
    import HtmlOverlayPreview from "./previews/HtmlOverlay";

    import lib from "remp/js/remplib.js";

    const props = [
        "name",
        "targetUrl",
        "position",
        "offsetVertical",
        "offsetHorizontal",
        "transition",
        "closeable",
        "closeText",
        "displayDelay",
        "closeTimeout",
        "targetSelector",
        "displayType",
        "template",
        "uuid",
        "campaignUuid",
        "forcedPosition",
        "show",

        "variables",
        "urlParams",

        "mediumRectangleTemplate",
        "barTemplate",
        "collapsibleBarTemplate",
        "htmlTemplate",
        "shortMessageTemplate",
        "overlayRectangleTemplate",
        "htmlOverlayTemplate",

        "alignmentOptions",
        "dimensionOptions",
        "positionOptions",

        "variantUuid",

        "adminPreview",

        "js",
        "jsIncludes",
        "cssIncludes",
    ];

    export default {
        components: {
            HtmlPreview,
            MediumRectanglePreview,
            BarPreview,
            CollapsibleBarPreview,
            ShortMessagePreview,
            OverlayRectanglePreview,
            HtmlOverlayPreview,
        },
        name: 'banner-preview',
        props: props,
        created: function() {
            this.$on('values-changed', function(data) {
                for (let item of data) {
                    this[item.key] = item.val;
                }
            });
        },
        mounted: function() {
            this.wrapperId = 'remp-banner-' + lib.uuidv4();

            props.forEach((prop) => {
                this[prop.slice(1)] = this[prop];
            });

            this.visible = this.show;

            if (this.cssIncludes) {
                for (let ii = 0; ii < this.cssIncludes.length; ii++) {
                    lib.loadStyle(this.cssIncludes[ii]);
                }
            }

            let vm = this,
                js = this.js,
                loadedScriptsCount = 0;

            if (this.jsIncludes) {
                for (let ii = 0; ii < this.jsIncludes.length; ii++) {
                    if (!this.jsIncludes[ii]) {
                        loadedScriptsCount++;
                        continue;
                    }

                    lib.loadScript(this.jsIncludes[ii], function () {
                        loadedScriptsCount++;
                        if (loadedScriptsCount === vm.jsIncludes.length) {
                            vm.runCustomJavascript(js);
                        }
                    });
                }
            } else {
                this.runCustomJavascript(js);
            }

            this.$nextTick(() => {
                let hrefs = document.getElementById(this.wrapperId).getElementsByTagName('a');

                for(let ii = 0; ii < hrefs.length; ii++) {
                    let href = hrefs[ii].getAttribute('href'),
                        linkParams = {};

                    [].forEach.call(hrefs[ii].attributes, function(attr) {
                        if (/^data-param-/.test(attr.name)) {
                            let camelCaseName = attr.name.substr(11).replace(/-(.)/g, function ($0, $1) {
                                return $1.toUpperCase();
                            });
                            linkParams[camelCaseName] = attr.value;
                        }
                    });

                    if (href) {
                        hrefs[ii].setAttribute('href', this.addUrlParams(href, linkParams));
                    }
                }
            }, this);
        },
        data: () => ({
            visible: false,
            wrapperId: null
        }),
        watch: {
            'transition': function() {
                let vm = this;
                setTimeout(function() { vm.visible = false }, 100);
                setTimeout(function() { vm.visible = true }, 800);
            },
            'show': function() {
            	this.visible = this.show;
            }
        },
        computed: {
            url: function() {
                if (this.targetUrl) {
                    return this.targetUrl;
                }
                return null;
            },
        },
        methods: {
            addUrlParams: function(url, linkParams) {
                let separator = url.indexOf("?") === -1 ? "?" : "&";
                url =  url + separator + "utm_source=remp_campaign" +
                    "&utm_medium=" + encodeURIComponent(this.displayType);
                if (this.campaignUuid) {
                    url += "&utm_campaign=" + encodeURIComponent(this.campaignUuid);
                }
                if (this.uuid) {
                    url += "&utm_content=" + encodeURIComponent(this.uuid);
                }
                if (this.variantUuid) {
                    url += "&banner_variant=" + encodeURIComponent(this.variantUuid);
                }

                if (this.urlParams) {
                    for (let param in this.urlParams) {
                        if (this.urlParams.hasOwnProperty(param)) {
                            url += "&" + encodeURIComponent(param) + '=' + encodeURIComponent(this.urlParams[param]())
                        }
                    }
                }

                if (linkParams) {
                    for (let param in linkParams) {
                        if (linkParams.hasOwnProperty(param)) {
                            url += "&" + encodeURIComponent(param) + '=' + encodeURIComponent(linkParams[param])
                        }
                    }
                }

                return url;
            },
            injectVars: function(str) {
                if (!remplib || !remplib.campaign) {
                    return str;
                }
                let re = /\{\{\s?(.*?)\s?\}\}/g;
                let match;

                while (match = re.exec(str)) {
                    let replRegex = new RegExp(match[0], "g");
                    let replVal = '';
                    if (remplib.campaign.variables && remplib.campaign.variables.hasOwnProperty(match[1])) {
                        replVal = remplib.campaign.variables[match[1]].value()
                    } else {
                        throw EvalError("cannot render banner, variable [" + match[1] + "] is missing");
                    }
                    str = str.replace(replRegex, replVal);
                }
                return str;
            },
            closed: function() {
				this.visible = false;
				this.$parent.$emit('values-changed', [
                    {key: "show", val: false}
                ]);
                if (this.closeTracked) {
                    return true;
                }
                this.trackEvent("banner", "close", null, null, {
                    "utm_source": "remp_campaign",
                    "utm_medium": this.displayType,
                    "utm_campaign": this.campaignUuid,
                    "utm_content": this.uuid,
                    "banner_variant": this.variantUuid
                });
                this.closeTracked = true;
            },
            clicked: function(event, hideBanner = false) {
                if (this.clickTracked) {
                    return true;
                }
                this.trackEvent("banner", "click", null, null, {
                    "utm_source": "remp_campaign",
                    "utm_medium": this.displayType,
                    "utm_campaign": this.campaignUuid,
                    "utm_content": this.uuid,
                    "banner_variant": this.variantUuid
                });
                this.clickTracked = true;
                if (hideBanner) {
                    this.visible = false;
                }
                return true;
            },
            trackEvent: function(category, action, tags, fields, source) {
                if (typeof remplib.tracker === 'undefined') {
                    return;
                }
                remplib.tracker.trackEvent(category, action, tags, fields, source);
            },
            customPositioned: function() {
                if (this.displayType === 'inline') {
                    return false;
                }
                if (this.displayType === 'overlay') {
                    return true;
                }
                if (this.forcedPosition !== undefined && this.forcedPosition === 'absolute') {
                    return true;
                }
                return false;
            },
            runCustomJavascript: function (js) {
                this.$nextTick(() => {
                    setTimeout(function() {
                        try {
                            eval('(function() {' + js + '})()');
                        } catch {
                            console.warn("unable to execute custom banner JS:", js);
                        }
                    }, 0);
                }, this)
            }
        }
    }
</script>


Propogating style updates from create modal to edit modal

<template>

  <core-modal
    :title="$tr('editUser')"
    @cancel="displayModal(false)"
    width="400px"
  >
    <form @submit.prevent="submitForm">

      <ui-alert
        v-if="error"
        type="error"
        :dismissible="false"
      >
        {{ error }}
      </ui-alert>

      <k-textbox
        ref="name"
        type="text"
        :label="$tr('fullName')"
        :autofocus="true"
        :maxlength="120"
        :invalid="nameIsInvalid"
        :invalidText="nameIsInvalidText"
        @blur="nameBlurred = true"
        v-model="newName"
      />

      <k-textbox
        ref="username"
        type="text"
        :label="$tr('username')"
        :maxlength="30"
        :invalid="usernameIsInvalid"
        :invalidText="usernameIsInvalidText"
        @blur="usernameBlurred = true"
        v-model="newUsername"
      />

      <k-select
        :label="$tr('userType')"
        :options="userKinds"
        v-model="newKind"
      />

      <fieldset class="coach-selector" v-if="coachIsSelected">
        <label>
          <k-radio-button
            :label="$tr('classCoachLabel')"
            :radiovalue="true"
            v-model="classCoachIsSelected"
          />
          {{ $tr('classCoachDescription') }}
        </label>
        <label>
          <k-radio-button
            :label="$tr('facilityCoachLabel')"
            :radiovalue="false"
            v-model="classCoachIsSelected"
          />
          {{ $tr('facilityCoachDescription') }}
        </label>
      </fieldset>

      <div class="core-modal-buttons">
        <k-button
          :text="$tr('cancel')"
          :primary="false"
          appearance="flat-button"
          @click="displayModal(false)"
        />
        <k-button
          type="submit"
          :text="$tr('save')"
          :primary="true"
          appearance="raised-button"
          :disabled="isBusy"
        />
      </div>
    </form>
  </core-modal>

</template>

<script>

  import { updateUser, displayModal } from '../../state/actions';
  import { UserKinds } from 'kolibri.coreVue.vuex.constants';
  import { currentFacilityId } from 'kolibri.coreVue.vuex.getters';
  import { validateUsername } from 'kolibri.utils.validators';
  import coreModal from 'kolibri.coreVue.components.coreModal';
  import kTextbox from 'kolibri.coreVue.components.kTextbox';
  import kButton from 'kolibri.coreVue.components.kButton';
  import kSelect from 'kolibri.coreVue.components.kSelect';
  import uiAlert from 'kolibri.coreVue.components.uiAlert';
  import kRadioButton from 'kolibri.coreVue.components.kRadioButton';

  export default {
    name: 'editUserModal',
    $trs: {
      editUser: 'Edit user',
      fullName: 'Full name',
      username: 'Username',
      userType: 'User type',
      admin: 'Admin',
      coach: 'Coach',
      learner: 'Learner',
      save: 'Save',
      cancel: 'Cancel',
      required: 'This field is required',
      usernameNotAlphaNumUnderscore: 'Username can only contain letters, numbers, and underscores',
      classCoachLabel: 'Class coach',
      classCoachDescription: "Can only instruct classes that they're assigned to",
      facilityCoachLabel: 'Facility coach',
      facilityCoachDescription: 'Can instruct all classes in your facility',
    },
    components: {
      kButton,
      coreModal,
      kTextbox,
      kSelect,
      uiAlert,
      kRadioButton,
    },
    props: {
      id: {
        type: String,
        required: true,
      },
      name: {
        type: String,
        required: true,
      },
      username: {
        type: String,
        required: true,
      },
      kind: {
        type: String,
        required: true,
      },
    },
    data() {
      return {
        classCoachIsSelected: true,
        newName: this.name,
        newUsername: this.username,
        newKind: null,
        nameBlurred: false,
        usernameBlurred: false,
        formSubmitted: false,
      };
    },
    computed: {
      coachIsSelected() {
        return this.newKind.value === UserKinds.COACH;
      },
      userKinds() {
        return [
          {
            label: this.$tr('learner'),
            value: UserKinds.LEARNER,
          },
          {
            label: this.$tr('coach'),
            value: UserKinds.COACH,
          },
          {
            label: this.$tr('admin'),
            value: UserKinds.ADMIN,
          },
        ];
      },
      nameIsInvalidText() {
        if (this.nameBlurred || this.formSubmitted) {
          if (this.newName === '') {
            return this.$tr('required');
          }
        }
        return '';
      },
      nameIsInvalid() {
        return Boolean(this.nameIsInvalidText);
      },
      usernameIsInvalidText() {
        if (this.usernameBlurred || this.formSubmitted) {
          if (this.newUsername === '') {
            return this.$tr('required');
          }
          if (!validateUsername(this.newUsername)) {
            return this.$tr('usernameNotAlphaNumUnderscore');
          }
        }
        return '';
      },
      usernameIsInvalid() {
        return Boolean(this.usernameIsInvalidText);
      },
      formIsValid() {
        return !this.nameIsInvalid && !this.usernameIsInvalid;
      },
    },
    beforeMount() {
      const coachOption = this.userKinds[1];
      if (this.kind === UserKinds.ASSIGNABLE_COACH) {
        this.newKind = coachOption;
        this.classCoachIsSelected = true;
      } else if (this.kind === UserKinds.COACH) {
        this.newKind = coachOption;
        this.classCoachIsSelected = false;
      } else {
        this.newKind = this.userKinds.find(kind => kind.value === this.kind);
      }
    },
    methods: {
      submitForm() {
        const roleUpdate = {
          collection: this.currentFacilityId,
        };
        this.formSubmitted = true;
        if (this.formIsValid) {
          if (this.newKind.value === UserKinds.COACH) {
            if (this.classCoachIsSelected) {
              roleUpdate.kind = UserKinds.ASSIGNABLE_COACH;
            } else {
              roleUpdate.kind = UserKinds.COACH;
            }
          } else {
            roleUpdate.kind = this.newKind.value;
          }
          this.updateUser(this.id, {
            username: this.newUsername,
            name: this.newName,
            role: roleUpdate,
          });
          if (
            this.currentUserId === this.id &&
            this.currentUserKind !== UserKinds.SUPERUSER &&
            this.newKind.value === UserKinds.LEARNER
          ) {
            window.location.href = window.location.origin;
          }
        } else {
          if (this.nameIsInvalid) {
            this.$refs.name.focus();
          } else if (this.usernameIsInvalid) {
            this.$refs.username.focus();
          }
        }
      },
    },
    vuex: {
      actions: {
        updateUser,
        displayModal,
      },
      getters: {
        currentFacilityId,
        currentUserId: state => state.core.session.user_id,
        currentUserKind: state => state.core.session.kind[0],
        error: state => state.pageState.error,
        isBusy: state => state.pageState.isBusy,
      },
    },
  };

</script>

<style lang="stylus" scoped>

.coach-selector
  margin-bottom: 3em
  margin: 0
  padding: 0
  border: none

</style>

<template>

  <core-modal
    :title="$tr('editUser')"
    @cancel="displayModal(false)"
    width="400px"
  >
    <form @submit.prevent="submitForm">

      <ui-alert
        v-if="error"
        type="error"
        :dismissible="false"
      >
        {{ error }}
      </ui-alert>

      <k-textbox
        ref="name"
        type="text"
        :label="$tr('fullName')"
        :autofocus="true"
        :maxlength="120"
        :invalid="nameIsInvalid"
        :invalidText="nameIsInvalidText"
        @blur="nameBlurred = true"
        v-model="newName"
      />

      <k-textbox
        ref="username"
        type="text"
        :label="$tr('username')"
        :maxlength="30"
        :invalid="usernameIsInvalid"
        :invalidText="usernameIsInvalidText"
        @blur="usernameBlurred = true"
        v-model="newUsername"
      />

      <k-select
        class="kind-select"
        :label="$tr('userType')"
        :options="userKinds"
        v-model="newKind"
      />

      <fieldset v-if="coachIsSelected">
        <label>
          <k-radio-button
            :label="$tr('classCoachLabel')"
            :radiovalue="true"
            v-model="classCoachIsSelected"
          />
          {{ $tr('classCoachDescription') }}
        </label>
        <label>
          <k-radio-button
            :label="$tr('facilityCoachLabel')"
            :radiovalue="false"
            v-model="classCoachIsSelected"
          />
          {{ $tr('facilityCoachDescription') }}
        </label>
      </fieldset>

      <div class="core-modal-buttons">
        <k-button
          :text="$tr('cancel')"
          :primary="false"
          appearance="flat-button"
          @click="displayModal(false)"
        />
        <k-button
          type="submit"
          :text="$tr('save')"
          :primary="true"
          appearance="raised-button"
          :disabled="isBusy"
        />
      </div>
    </form>
  </core-modal>

</template>

<script>

  import { updateUser, displayModal } from '../../state/actions';
  import { UserKinds } from 'kolibri.coreVue.vuex.constants';
  import { currentFacilityId } from 'kolibri.coreVue.vuex.getters';
  import { validateUsername } from 'kolibri.utils.validators';
  import coreModal from 'kolibri.coreVue.components.coreModal';
  import kTextbox from 'kolibri.coreVue.components.kTextbox';
  import kButton from 'kolibri.coreVue.components.kButton';
  import kSelect from 'kolibri.coreVue.components.kSelect';
  import uiAlert from 'kolibri.coreVue.components.uiAlert';
  import kRadioButton from 'kolibri.coreVue.components.kRadioButton';

  export default {
    name: 'editUserModal',
    $trs: {
      editUser: 'Edit user',
      fullName: 'Full name',
      username: 'Username',
      userType: 'User type',
      admin: 'Admin',
      coach: 'Coach',
      learner: 'Learner',
      save: 'Save',
      cancel: 'Cancel',
      required: 'This field is required',
      usernameNotAlphaNumUnderscore: 'Username can only contain letters, numbers, and underscores',
      classCoachLabel: 'Class coach',
      classCoachDescription: "Can only instruct classes that they're assigned to",
      facilityCoachLabel: 'Facility coach',
      facilityCoachDescription: 'Can instruct all classes in your facility',
    },
    components: {
      kButton,
      coreModal,
      kTextbox,
      kSelect,
      uiAlert,
      kRadioButton,
    },
    props: {
      id: {
        type: String,
        required: true,
      },
      name: {
        type: String,
        required: true,
      },
      username: {
        type: String,
        required: true,
      },
      kind: {
        type: String,
        required: true,
      },
    },
    data() {
      return {
        classCoachIsSelected: true,
        newName: this.name,
        newUsername: this.username,
        newKind: null,
        nameBlurred: false,
        usernameBlurred: false,
        formSubmitted: false,
      };
    },
    computed: {
      coachIsSelected() {
        return this.newKind.value === UserKinds.COACH;
      },
      userKinds() {
        return [
          {
            label: this.$tr('learner'),
            value: UserKinds.LEARNER,
          },
          {
            label: this.$tr('coach'),
            value: UserKinds.COACH,
          },
          {
            label: this.$tr('admin'),
            value: UserKinds.ADMIN,
          },
        ];
      },
      nameIsInvalidText() {
        if (this.nameBlurred || this.formSubmitted) {
          if (this.newName === '') {
            return this.$tr('required');
          }
        }
        return '';
      },
      nameIsInvalid() {
        return Boolean(this.nameIsInvalidText);
      },
      usernameIsInvalidText() {
        if (this.usernameBlurred || this.formSubmitted) {
          if (this.newUsername === '') {
            return this.$tr('required');
          }
          if (!validateUsername(this.newUsername)) {
            return this.$tr('usernameNotAlphaNumUnderscore');
          }
        }
        return '';
      },
      usernameIsInvalid() {
        return Boolean(this.usernameIsInvalidText);
      },
      formIsValid() {
        return !this.nameIsInvalid && !this.usernameIsInvalid;
      },
    },
    beforeMount() {
      const coachOption = this.userKinds[1];
      if (this.kind === UserKinds.ASSIGNABLE_COACH) {
        this.newKind = coachOption;
        this.classCoachIsSelected = true;
      } else if (this.kind === UserKinds.COACH) {
        this.newKind = coachOption;
        this.classCoachIsSelected = false;
      } else {
        this.newKind = this.userKinds.find(kind => kind.value === this.kind);
      }
    },
    methods: {
      submitForm() {
        const roleUpdate = {
          collection: this.currentFacilityId,
        };
        this.formSubmitted = true;
        if (this.formIsValid) {
          if (this.newKind.value === UserKinds.COACH) {
            if (this.classCoachIsSelected) {
              roleUpdate.kind = UserKinds.ASSIGNABLE_COACH;
            } else {
              roleUpdate.kind = UserKinds.COACH;
            }
          } else {
            roleUpdate.kind = this.newKind.value;
          }
          this.updateUser(this.id, {
            username: this.newUsername,
            name: this.newName,
            role: roleUpdate,
          });
          if (
            this.currentUserId === this.id &&
            this.currentUserKind !== UserKinds.SUPERUSER &&
            this.newKind.value === UserKinds.LEARNER
          ) {
            window.location.href = window.location.origin;
          }
        } else {
          if (this.nameIsInvalid) {
            this.$refs.name.focus();
          } else if (this.usernameIsInvalid) {
            this.$refs.username.focus();
          }
        }
      },
    },
    vuex: {
      actions: {
        updateUser,
        displayModal,
      },
      getters: {
        currentFacilityId,
        currentUserId: state => state.core.session.user_id,
        currentUserKind: state => state.core.session.kind[0],
        error: state => state.pageState.error,
        isBusy: state => state.pageState.isBusy,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  .kind-select
    margin-bottom: 32px

</style>


prevent error when loading the editor with a query without an id

<template>

    <template v-if="!isDatasetLoaded">
      <SkeletonSpinner
        height-square="300px"
        squares-rows="2"
        squares="2"
        lines="5"
      />
    </template>
    <template v-else>

            {{ titleDataset }}

      <DatasetNav :active-dataset-tab="activeDatasetTab" />

      <SummaryTab
        v-if="activeDatasetTab === 0 && attributes"
        :dataset-id="datasetId"
        :is-viz-modified="isVizModified"
        :is-viz-item-modified="isVizItemModified"
        :is-viz-saved="isVizSaved"
        :is-private-viz-loading="isPrivateVizLoading"
        :is-public-viz-loading="isPublicVizLoading"
        :public-visualizations="publicVisualizations"
        :private-visualizations="privateVisualizations"
        :enabled-viz-saved-button="enabledVizSavedButton"
        :current-viz-tab="currentVizTab"
        :enabled-fork-viz-button="enabledForkVizButton"
        :viz-input-focus="vizInputFocus"
        :viz-id="vizID"
        :user-save-viz="userSaveViz"
        :show-private-public-icon-viz="showPrivatePublicIconViz"
        :show-private="showPrivate"
        :show-private-viz="showPrivateViz"
        :show-label-edit="showLabelEdit"
        :reset-private="resetPrivate"
        :private-queries="privateQueries"
        :public-queries="publicQueries"
        :array-formats="arrayFormats"
        :object-columns="objectColumns"
        :resources-list="resourcesList"
        :dataset-attributes="attributes"
        :is-user-logged="isUserLogged"
      />

      <DataTab
        v-else-if="activeDatasetTab === 1"
        :private-queries="privateQueries"
        :public-queries="publicQueries"
        :recent-queries="recentQueriesFiltered"
        :object-columns="objectColumns"
        :array-formats="arrayFormats"
        :array-columns-query="arrayColumnsQuery"
        :items="items"
        :is-query-saved="isQuerySaved"
        :is-viz-saved="isVizSaved"
        :is-query-saving-prompt-visible="isQuerySavingPromptVisible"
        :is-query-running="isQueryRunning"
        :is-query-modified="isQueryModified"
        :is-viz-modified="isVizModified"
        :is-viz-saving-prompt-visible="isVizSavingPromptVisible"
        :is-fork-prompt-visible="isForkPromptVisible"
        :query-stored="currentQuery"
        :query-name="queryName"
        :query-duration="queryDuration"
        :query-error="queryError"
        :enabled-query-saved-button="enabledQuerySavedButton"
        :enabled-viz-saved-button="enabledVizSavedButton"
        :enabled-fork-button="enabledForkButton"
        :enabled-revert-button="enabledRevertButton"
        :show-revert-query="showRevertQuery"
        :show-private="showPrivate"
        :table-name="tableName"
        :is-user-logged="isUserLogged"
        :query-input-focus="queryInputFocus"
        :viz-input-focus="vizInputFocus"
        :viz-name="vizName"
        :viz-id="vizID"
        :reset-private="resetPrivate"
        :show-private-public-icon="showPrivatePublicIcon"
        :show-private-public-icon-viz="showPrivatePublicIconViz"
      />

      <QueriesTab
        v-else-if="activeDatasetTab === 2"
        :private-queries="privateQueries"
        :public-queries="publicQueries"
        :is-user-logged="isUserLogged"
      />

      <VisualizationsTab
        v-else-if="activeDatasetTab === 3"
        :dataset-id="datasetId"
        :is-user-logged="isUserLogged"
        :is-viz-saving-prompt-visible="isVizSavingPromptVisible"
        :is-viz-modified="isVizModified"
        :is-viz-item-modified="isVizItemModified"
        :is-viz-saved="isVizSaved"
        :is-private-viz-loading="isPrivateVizLoading"
        :is-public-viz-loading="isPublicVizLoading"
        :public-visualizations="publicVisualizations"
        :private-visualizations="privateVisualizations"
        :private-queries="privateQueries"
        :public-queries="publicQueries"
        :enabled-viz-saved-button="enabledVizSavedButton"
        :current-viz-tab="currentVizTab"
        :enabled-fork-viz-button="enabledForkVizButton"
        :viz-input-focus="vizInputFocus"
        :viz-id="vizID"
        :user-save-viz="userSaveViz"
        :show-private-public-icon-viz="showPrivatePublicIconViz"
        :show-private="showPrivate"
        :show-private-viz="showPrivateViz"
        :show-label-edit="showLabelEdit"
        :reset-private="resetPrivate"
        :object-columns="objectColumns"
      />

      <DownloadsTab
        v-else-if="activeDatasetTab === 4"
        :array-formats="arrayFormats"
        :resources-list="resourcesList"
      />
    </template>

</template>


Support more speeds

Support more speeds

<template>

{{game.time_played | moment("MMM Do, YY - HH:mm:ss")}}

              <span :class="`player color-${sortedPlayers[0].index + 1}`" v-if="sortedPlayers.length">
                <TierPopover :tier="tierClass(sortedPlayers[0].tier)"/>
                <a v-if="sortedPlayers[0].id" class="player-name-anchor" :href="`/user/?user_id=${sortedPlayers[0].id}`">{{sortedPlayers[0].name}}
                {{sortedPlayers[0].name}}

              defeats
              <span :class="`player color-${sortedPlayers[1].index + 1}`" v-if="sortedPlayers.length">
                <TierPopover :tier="tierClass(sortedPlayers[1].tier)"/>
                <a class="player-name-anchor" :href="`/user/?user_id=${sortedPlayers[1].id}`">{{sortedPlayers[1].name}}

               2">+{{sortedPlayers.length - 2}}

              <span :class="`player color-${sortedPlayers[0].index + 1}`" v-if="sortedPlayers.length">
                <TierPopover :tier="tierClass(sortedPlayers[0].tier)"/>
                <a v-if="sortedPlayers[0].id" class="player-name-anchor" :href="`/user/?user_id=${sortedPlayers[0].id}`">{{sortedPlayers[0].name}}
                {{sortedPlayers[0].name}}

              defeats
              <span :class="`player color-${player.index + 1}`" v-for="(player, index) in sortedPlayers" v-if="index > 0" :key="index">
                <TierPopover :tier="tierClass(player.tier)"/>
                <a v-if="player.id" class="player-name-anchor" :href="`/user/?user_id=${player.id}`">{{player.name}}
                <span v-if="!player.id" class="player-name-anchor" :href="`/user/?user_id=${player.id}`">{{player.name}}

0

<vue-slider v-model="frame" v-bind="sliderOptions" @callback="changeFrame"></vue-slider>

{{sliderOptions.max}}

                    Share as a link

                      <input ref="shareInput" type="text" :value="shareLink">
                      <button class="btn" @click="copyToClipboard">
                        Copy
                      </button>

                <button class="btn" @click="toggleShare">
                  SHARE
                </button>

                Player stats

                Game/Map Stats

                <PlayerStatsPane :players="players" :statistics="statistics" :userlink="userlink"></PlayerStatsPane>

                      Map Size:
                      {{`${replay.width}x${replay.height}`}}

                      Map Parameters:
                      {{replay.map_generator}}

                      Seed:
                      {{replay.seed}}

                      Replay Version:
                      {{replay.version}}

player details

              <PlayerDetailPane :replay="replay" :statistics="statistics" :stats="stats" :frame="frame"></PlayerDetailPane>

map object properties

                <SelectedPlanet :selected-planet="selectedPlanet" :players="players"></SelectedPlanet>

                <SelectedShip :selected-ship="selectedShip" :players="players"></SelectedShip>

                <SelectedPoint :selected-point="selectedPoint"></SelectedPoint>

Click on a ship, planet, or other map location to see properties

post game dashboard

                      <img :src="`https://github.com/${_player.name}.png`">

                      <span style="display: block;" :class="`player color-${_pIndex + 1}`">
                        <TierPopover :tier="tierClass(_player.tier)"/>
                        RANK {{_player.rank}}

                        <a v-if="_player.id" class="player-name-anchor" :href="`/user/?user_id=${_player.id}`">{{_player.name}}
                        {{_player.name}}

Bot version:

Local bot

V{{_player.version}}

                      Ships

                    <PlayerLineChart ref="chart2" :selected-players="selectedPlayers" :chart-data="chartData.ship" :index="frame" :showChart="showChartPanel" @updateIndex="index => {frame = index}" />

                      health

                    <PlayerLineChart ref="chart3" :selected-players="selectedPlayers" :chart-data="chartData.health" :index="frame" :showChart="showChartPanel" @updateIndex="index => {frame = index}" />

                      damage dealt

                    <PlayerLineChart ref="chart4" :selected-players="selectedPlayers" :chart-data="chartData.damage" :index="frame" :showChart="showChartPanel" @updateIndex="index => {frame = index}" />

                      attack over time

                    <PlayerLineChart ref="chart5" :selected-players="selectedPlayers" :chart-data="chartData.attack" :index="frame" :showChart="showChartPanel" @updateIndex="index => {frame = index}" />

</template>


allow zero as send offline nonce

allow zero as send offline nonce

<template>

    <interface-container-title :title="$t('common.offline')" />

{{ $t('interface.sendTxType') }}

                <currency-picker
                  :currency="allTokens"
                  :token="true"
                  page="sendOfflineGenTx"
                  @selectedCurrency="setSelectedCurrency"
                />

{{ $t('interface.sendTxAmount') }}

                  <input
                    :value="toAmt"
                    :placeholder="$t('interface.depAmount')"
                    type="number"
                    step="any"
                    name
                    @input="debouncedAmount"
                  />

{{ $t('interface.sendTxToAddr') }}

                <blockie
                  v-show="address !== '' && validAddress"
                  :address="address"
                  class="blockie-image-icon"
                  width="32px"
                  height="32px"
                />
                <button
                  class="title-button copy-button prevent-user-select"
                  @click="copyToAddress"
                >
                  {{ $t('common.copy') }}
                </button>

                <textarea
                  ref="toaddress"
                  v-model="address"
                  name="name"
                  placeholder="Please Enter The Address"
                />

{{ $t('common.data') }}

            <input
              v-model="toData"
              :disabled="selectedCoinType.symbol !== 'ETH'"
              type="string"
              placeholder="e.g. 0x65746865726d696e652d657531"
            />

{{ $t('common.gasLimit') }}

                <popover :popcontent="$t('popover.gasLimit')" />

            <input
              v-model="gasLimit"
              :placeholder="$t('common.gasLimit')"
              type="number"
            />

               0 ? '' : 'not-good'
                ]"
                aria-hidden="true"
              />

{{ $t('common.nonce') }}

                <popover :popcontent="$t('popover.nonce')" />

            <input
              v-model="nonce"
              :placeholder="$t('common.nonce')"
              type="number"
            />

              = 0 ? '' : 'not-good'
                ]"
                aria-hidden="true"
              />

{{ $t('common.gasPrice') }}

                <popover :popcontent="txSpeedMsg" />

            <input
              v-model="localGasPrice"
              :placeholder="$t('common.gasPrice')"
              type="number"
            />

               0 ? '' : 'not-good'
                ]"
                aria-hidden="true"
              />

          <input
            ref="jsonInput"
            type="file"
            name="file"
            style="display: none"
            @change="uploadFile"
          />

            Import JSON

            {{ $t('interface.generateTx') }}

          <interface-bottom-text
            link="https://kb.myetherwallet.com"
            question="Have issues?"
            link-text="Help Center"
          />

      <signed-tx-modal ref="signedTxModal" :signed-tx="signed" :raw-tx="raw" />

</template>

<template>

    <interface-container-title :title="$t('common.offline')" />

{{ $t('interface.sendTxType') }}

                <currency-picker
                  :currency="allTokens"
                  :token="true"
                  page="sendOfflineGenTx"
                  @selectedCurrency="setSelectedCurrency"
                />

{{ $t('interface.sendTxAmount') }}

                  <input
                    :value="toAmt"
                    :placeholder="$t('interface.depAmount')"
                    type="number"
                    step="any"
                    name
                    @input="debouncedAmount"
                  />

{{ $t('interface.sendTxToAddr') }}

                <blockie
                  v-show="address !== '' && validAddress"
                  :address="address"
                  class="blockie-image-icon"
                  width="32px"
                  height="32px"
                />
                <button
                  class="title-button copy-button prevent-user-select"
                  @click="copyToAddress"
                >
                  {{ $t('common.copy') }}
                </button>

                <textarea
                  ref="toaddress"
                  v-model="address"
                  name="name"
                  placeholder="Please Enter The Address"
                />

{{ $t('common.data') }}

            <input
              v-model="toData"
              :disabled="selectedCoinType.symbol !== 'ETH'"
              type="string"
              placeholder="e.g. 0x65746865726d696e652d657531"
            />

{{ $t('common.gasLimit') }}

                <popover :popcontent="$t('popover.gasLimit')" />

            <input
              v-model="gasLimit"
              :placeholder="$t('common.gasLimit')"
              type="number"
            />

               0 ? '' : 'not-good'
                ]"
                aria-hidden="true"
              />

{{ $t('common.nonce') }}

                <popover :popcontent="$t('popover.nonce')" />

            <input
              v-model="nonce"
              :placeholder="$t('common.nonce')"
              type="number"
            />

               0 ? '' : 'not-good'
                ]"
                aria-hidden="true"
              />

{{ $t('common.gasPrice') }}

                <popover :popcontent="txSpeedMsg" />

            <input
              v-model="localGasPrice"
              :placeholder="$t('common.gasPrice')"
              type="number"
            />

               0 ? '' : 'not-good'
                ]"
                aria-hidden="true"
              />

          <input
            ref="jsonInput"
            type="file"
            name="file"
            style="display: none"
            @change="uploadFile"
          />

            Import JSON

            {{ $t('interface.generateTx') }}

          <interface-bottom-text
            link="https://kb.myetherwallet.com"
            question="Have issues?"
            link-text="Help Center"
          />

      <signed-tx-modal ref="signedTxModal" :signed-tx="signed" :raw-tx="raw" />

</template>


chore(): change back to .json instead of .webmanifest

<template>

    <HubHeader :showSubHeader="true"></HubHeader>

{{ $t("generate.subtitle") }}

{{ $t("generate.instructions") }}

              <button v-bind:class="{ active: showBasicSection }" @click="showBasicsSection()">Info</button>
              <button
                v-bind:class="{ active: showImagesSection }"
                @click="showImageSection()"
              >Images</button>
              <button
                v-bind:class="{ active: showSettingsSection }"
                @click="showSettingSection()"
              >Settings</button>

{{ $t("generate.name") }}

Used for App lists or Store listings

              <input
                class="l-generator-input"
                v-model="manifest$.name"
                @change="onChangeSimpleInput()"
                type="text"
                v-on:focus="activeFormField = 'appName'"
                placeholder="App Name"
              />

{{ $t("generate.short_name") }}

Used for tiles or home screens

              <input
                class="l-generator-input"
                v-model="manifest$.short_name"
                @change="onChangeSimpleInput()"
                name="short_name"
                type="text"
                v-on:focus="activeFormField = 'shortName'"
                placeholder="App Short Name"
              />

{{ $t("generate.description") }}

Used for App listings

              <textarea
                id="descText"
                class="l-generator-textarea"
                v-model="manifest$.description"
                @keydown.enter.exact.prevent="textareaError"
                @keypress="textareaCheck"
                @change="onChangeSimpleInput()"
                name="description"
                type="text"
                v-on:focus="activeFormField = 'appDesc'"
                placeholder="App Description"
                v-bind:style="{ outline: textareaOutlineColor}"
              ></textarea>
              Newline not allowed

{{ $t("generate.start_url") }}

This will be the first page that loads in your PWA.

              <input
                class="l-generator-input"
                v-model="manifest$.start_url"
                @change="onChangeSimpleInput()"
                type="text"
                v-on:focus="activeFormField = 'startURL'"
                placeholder="Start URL"
              />

Upload app icons for your PWA

We suggest at least one image 512×512 or larger

                    <button
                      id="iconUploadButton"
                      class="work-button l-generator-button"
                      @click="onClickUploadIcon()"
                    >Upload</button>

{{icon.sizes}}

{{ $t("generate.scope") }}

Scope determines what part of your website runs in the PWA

              <input
                class="l-generator-input"
                v-model="manifest$.scope"
                @change="onChangeSimpleInput()"
                type="text"
                placeholder="App Scope"
                v-on:focus="activeFormField = 'appScope'"
              />

{{ $t("generate.display") }}

Display identifies the browser components that should be included in your. "Standalone" appears as a traditional app.

              <select
                class="l-generator-input l-generator-input--select"
                v-model="manifest$.display"
                @change="onChangeSimpleInput()"
                v-on:focus="activeFormField = 'displayMode'"
              >
                <option v-for="display in displaysNames" :value="display" :key="display">{{display}}</option>
              </select>

{{ $t("generate.orientation") }}

Orientation determines the perfered flow of your application.

              <select
                class="l-generator-input l-generator-input--select"
                v-model="manifest$.orientation"
                @change="onChangeSimpleInput()"
                v-on:focus="activeFormField = 'appOrientation'"
              >
                <option
                  v-for="orientation in orientationsNames"
                  :value="orientation"
                  :key="orientation"
                >{{orientation}}</option>
              </select>

{{ $t("generate.language") }}

Declare the language of your PWA

              <select
                class="l-generator-input l-generator-input--select"
                v-model="manifest$.lang"
                @change="onChangeSimpleInput()"
                v-on:change="activeFormField = 'appLang'"
              >
                <option
                  v-for="language in languagesNames"
                  :value="language"
                  :key="language"
                >{{language}}</option>
              </select>

              <ColorSelector />

              <input
                type="checkbox"
                id="related-applications-field"
                class="l-generator-togglecheck is-hidden"
              />

{{ $t("generate.specify_application") }}

                <RelatedApplications />

          <nuxt-link @click.native="saveChanges" id="doneButton" to="reportCard">Done</nuxt-link>

        <CodeViewer
          code-type="html"
          v-if="seeEditor"
          title="Add this code to your start page"
          code="<link rel='manifest' href='/manifest.json'>"
          :showHeader="true"
          :showCopyButton="true"
          id="manifestHTML"
        >

Add this code to your start page:

        </CodeViewer>

        <CodeViewer
          code-type="json"
          v-on:invalidManifest="invalidManifest()"
          v-on:editorValue="handleEditorValue($event)"
          v-if="seeEditor"
          :code="getCode()"
          :title="$t('generate.w3c_manifest')"
          :suggestions="suggestions"
          :suggestionsTotal="suggestionsTotal"
          :warnings="warnings"
          :warningsTotal="warningsTotal"
          :showToolbar="true"
          :showHeader="true"
          :showCopyButton="showCopy"
          id="manifestCode"
        >

Add this code to your manifest.json file

        </CodeViewer>

      <Modal
        v-on:modalOpened="modalOpened()"
        v-on:modalClosed="modalClosed()"
        :title="$t('generate.upload_title')"
        ref="iconsModal"
        v-on:modalSubmit="onSubmitIconModal"
        v-on:cancel="onCancelIconModal"
      >

            {{ $t("generate.upload_image") }}
            {{ iconFile && iconFile.name ? iconFile.name : $t("generate.choose_file") }}
            <input id="modal-file" @change="onFileIconChange" class="l-hidden" type="file" />

              {{ $t("generate.generate_missing") }}
              <input
                type="checkbox"
                v-model="iconCheckMissing"
              />

      </Modal>

        PWA Builder was founded by Microsoft as a community guided, open source project to help move PWA adoption forward.
        Our Privacy Statement

</template>

<template>

    <HubHeader :showSubHeader="true"></HubHeader>

{{ $t("generate.subtitle") }}

{{ $t("generate.instructions") }}

              <button v-bind:class="{ active: showBasicSection }" @click="showBasicsSection()">Info</button>
              <button
                v-bind:class="{ active: showImagesSection }"
                @click="showImageSection()"
              >Images</button>
              <button
                v-bind:class="{ active: showSettingsSection }"
                @click="showSettingSection()"
              >Settings</button>

{{ $t("generate.name") }}

Used for App lists or Store listings

              <input
                class="l-generator-input"
                v-model="manifest$.name"
                @change="onChangeSimpleInput()"
                type="text"
                v-on:focus="activeFormField = 'appName'"
                placeholder="App Name"
              />

{{ $t("generate.short_name") }}

Used for tiles or home screens

              <input
                class="l-generator-input"
                v-model="manifest$.short_name"
                @change="onChangeSimpleInput()"
                name="short_name"
                type="text"
                v-on:focus="activeFormField = 'shortName'"
                placeholder="App Short Name"
              />

{{ $t("generate.description") }}

Used for App listings

              <textarea
                id="descText"
                class="l-generator-textarea"
                v-model="manifest$.description"
                @keydown.enter.exact.prevent="textareaError"
                @keypress="textareaCheck"
                @change="onChangeSimpleInput()"
                name="description"
                type="text"
                v-on:focus="activeFormField = 'appDesc'"
                placeholder="App Description"
                v-bind:style="{ outline: textareaOutlineColor}"
              ></textarea>
              Newline not allowed

{{ $t("generate.start_url") }}

This will be the first page that loads in your PWA.

              <input
                class="l-generator-input"
                v-model="manifest$.start_url"
                @change="onChangeSimpleInput()"
                type="text"
                v-on:focus="activeFormField = 'startURL'"
                placeholder="Start URL"
              />

Upload app icons for your PWA

We suggest at least one image 512×512 or larger

                    <button
                      id="iconUploadButton"
                      class="work-button l-generator-button"
                      @click="onClickUploadIcon()"
                    >Upload</button>

{{icon.sizes}}

{{ $t("generate.scope") }}

Scope determines what part of your website runs in the PWA

              <input
                class="l-generator-input"
                v-model="manifest$.scope"
                @change="onChangeSimpleInput()"
                type="text"
                placeholder="App Scope"
                v-on:focus="activeFormField = 'appScope'"
              />

{{ $t("generate.display") }}

Display identifies the browser components that should be included in your. "Standalone" appears as a traditional app.

              <select
                class="l-generator-input l-generator-input--select"
                v-model="manifest$.display"
                @change="onChangeSimpleInput()"
                v-on:focus="activeFormField = 'displayMode'"
              >
                <option v-for="display in displaysNames" :value="display" :key="display">{{display}}</option>
              </select>

{{ $t("generate.orientation") }}

Orientation determines the perfered flow of your application.

              <select
                class="l-generator-input l-generator-input--select"
                v-model="manifest$.orientation"
                @change="onChangeSimpleInput()"
                v-on:focus="activeFormField = 'appOrientation'"
              >
                <option
                  v-for="orientation in orientationsNames"
                  :value="orientation"
                  :key="orientation"
                >{{orientation}}</option>
              </select>

{{ $t("generate.language") }}

Declare the language of your PWA

              <select
                class="l-generator-input l-generator-input--select"
                v-model="manifest$.lang"
                @change="onChangeSimpleInput()"
                v-on:change="activeFormField = 'appLang'"
              >
                <option
                  v-for="language in languagesNames"
                  :value="language"
                  :key="language"
                >{{language}}</option>
              </select>

              <ColorSelector />

              <input
                type="checkbox"
                id="related-applications-field"
                class="l-generator-togglecheck is-hidden"
              />

{{ $t("generate.specify_application") }}

                <RelatedApplications />

          <nuxt-link @click.native="saveChanges" id="doneButton" to="reportCard">Done</nuxt-link>

        <CodeViewer
          code-type="html"
          v-if="seeEditor"
          title="Add this code to your start page"
          code="<link rel='manifest' href='/manifest.webmanifest'>"
          :showHeader="true"
          :showCopyButton="true"
          id="manifestHTML"
        >

Add this code to your start page:

        </CodeViewer>

        <CodeViewer
          code-type="json"
          v-on:invalidManifest="invalidManifest()"
          v-on:editorValue="handleEditorValue($event)"
          v-if="seeEditor"
          :code="getCode()"
          :title="$t('generate.w3c_manifest')"
          :suggestions="suggestions"
          :suggestionsTotal="suggestionsTotal"
          :warnings="warnings"
          :warningsTotal="warningsTotal"
          :showToolbar="true"
          :showHeader="true"
          :showCopyButton="showCopy"
          id="manifestCode"
        >

Add this code to your manifest.webmanifest file

        </CodeViewer>

      <Modal
        v-on:modalOpened="modalOpened()"
        v-on:modalClosed="modalClosed()"
        :title="$t('generate.upload_title')"
        ref="iconsModal"
        v-on:modalSubmit="onSubmitIconModal"
        v-on:cancel="onCancelIconModal"
      >

            {{ $t("generate.upload_image") }}
            {{ iconFile && iconFile.name ? iconFile.name : $t("generate.choose_file") }}
            <input id="modal-file" @change="onFileIconChange" class="l-hidden" type="file" />

              {{ $t("generate.generate_missing") }}
              <input
                type="checkbox"
                v-model="iconCheckMissing"
              />

      </Modal>

        PWA Builder was founded by Microsoft as a community guided, open source project to help move PWA adoption forward.
        Our Privacy Statement

</template>


The format of a date of interactions unify to it of posts

<template>

              <nuxt-link :to="`@${user.username}`">

              </nuxt-link>

          <nuxt-link v-if="post" :to="`@${action.objects[0].user.username}/posts/${action.objects[0].id}`">
            This post
          </nuxt-link>
          {{actionBy}}

              <nuxt-link :to="`@${user.username}`">
                @{{user.username}}
                <span v-if="i < action.users.length - 1">,
              </nuxt-link>

.

            <post class="mt-3" :data="post" view-only preview v-if="post" />

                {{date}}

</template>


style(button): prettier

style(button): prettier

<template>

        <mdc-button v-bind="buttonProps">Button</mdc-button>
        <mdc-button v-bind="buttonProps">Button</mdc-button>
        <mdc-button v-bind="buttonProps">Button</mdc-button>

        <mdc-radio
          v-model="type"
          label="baseline"
          name="button-type"
          checked/>
        <mdc-radio
          v-model="type"
          label="raised"
          name="button-type"/>
        <mdc-radio
          v-model="type"
          label="unelevated"
          name="button-type"/>
        <mdc-radio
          v-model="type"
          label="outlined"
          name="button-type"/>

        <mdc-checkbox
          v-model="disabled"
          label="disabled"/>
        <mdc-checkbox
          v-model="dense"
          label="dense"/>

</template>


OCC-388: Fixes #108. Ensure on sync for formats, that value is also updated when format is pattern.

<template>
  <form class="navbar-form form-horizontal" id="columnProperties">

          {{formprop.label}}

        <component :is="formprop.tooltipView"/>
        <template v-if="typeof formprop.type && formprop.type === 'dropdown'">
          <select v-if="formprop.key==='type'" :value="getTypeProperty" v-model="typeProperty" @input="setTypeProperty($event.target.value)" :id="formprop.key" class="form-control input-sm col-sm-9">
            <option v-for="option1 in typeValues" :key="option1" v-bind:value="option1">
              {{ option1}}
            </option>
          </select>

            <select :value="getFormatProperty" v-model="formatProperty" @input="setFormatProperty($event.target.value)" id="format" :disabled="isDropdownFormatDisabled" class="form-control input-sm col-sm-9">
                <option v-for="option2 in formatPropertiesForType" :key="option2" v-bind:value="option2">
                  {{ option2}}
                </option>
            </select>
            <input v-show="formatValuesHasPattern" v-model="formatPropertyValue" type="text" class="form-control input-sm col-sm-9"/>

        </template>

            <input type="checkbox" :id="option" :checked="getConstraintCheck(option)" @click="setConstraintCheck(option, $event.target)"></input>
            {{option}}
            <template v-if="!isBooleanConstraint(option) && getConstraintCheck(option)">
              <input type="text" :class="{ 'form-group-sm constraint-text': true,'validate-danger': errors.has(option) }" :value="getConstraintValue(option)" @input="setConstraintValue(option, $event.target.value)" v-validate="constraintValidationRules(option)" :name="option"/>
            </template>

              {{ errors.first(option)}}

        <input v-else-if="formprop.key === 'name'" :disabled="formprop.isDisabled" :value="getNameProperty" @input="setProperty(formprop.key, $event.target.value)" type="text" class="form-control label-sm col-sm-9" :id="formprop.key" />
        <input v-else :disabled="formprop.isDisabled" :value="getProperty(formprop.key)" @input="setProperty(formprop.key, $event.target.value)" type="text" class="form-control label-sm col-sm-9" :id="formprop.key" />

</form>
</template>


still working on search

still working on search

<template>

      All Users

     ( {{ users.length }} )

      <select v-model="roleFilter" name="user-filter">
        <option selected value="all"> All Users </option>
        <option v-bind:value="role" v-for="role in roles">
          {{role.kind | capitalize}}s
        </option>
        <option value="learner"> Learners </option>
      </select>

        <svg class="search-button" src="./icons/search.svg"></svg>
        <input v-model="searchFilter" type="search">

        <user-create-modal></user-create-modal>

           Student Name
           Username
           Edit

            {{user.first_name}} {{user.last_name}}

              {{role.kind | capitalize}}

            {{user.username}}

            <user-edit-modal
              :userid="user.id"
              :roles="user.roles"
              :username="user.username"
              :firstname="user.first_name"
              :lastname="user.last_name">
            </user-edit-modal>

</template>

<template>

      All Users

     ( {{ users.length }} )

      <select v-model="roleFilter" name="user-filter">
        <option selected value="all"> All Users </option>
        <option v-bind:value="role" v-for="role in roles">
          {{role.kind | capitalize}}s
        </option>
        <option value="learner"> Learners </option>
      </select>

        <input v-model="searchFilter" type="search">
        <svg class="search-button" src="./icons/search.svg"></svg>

        <user-create-modal></user-create-modal>

           Student Name
           Username
           Edit

            {{user.first_name}} {{user.last_name}}

              {{role.kind | capitalize}}

            {{user.username}}

            <user-edit-modal
              :userid="user.id"
              :roles="user.roles"
              :username="user.username"
              :firstname="user.first_name"
              :lastname="user.last_name">
            </user-edit-modal>

</template>