<commit_before>const qu = require('async/queue');
const co = require('co');
const moment = require('moment');
const _ = require('lodash');

const constants = require('../common/constants.js');
const ga = require('../common/ga.js');
const cache = require('../common/data-cache.js')();
const serviceUrl = `https://${constants.hostname}`;

const alarmPeriodMinutes = 12 * 60;
const alarmKey = 'macheteSync';
const entityIdKey = 'spEntityIds';
const sellerDomainKey = 'sellerDomains';
const lastVersionKey = 'lastVersion';

chrome.runtime.onInstalled.addListener(details => {
    const manifest = chrome.runtime.getManifest();
    if (details.reason == 'install') {
        chrome.tabs.create({ url: `${serviceUrl}/${process.env.PRODUCT}/welcome` });
    }
    localStorage.setItem(lastVersionKey, manifest.version);
    setAlarm();
});

chrome.pageAction.onClicked.addListener(ga.mcatch(() => {
    chrome.tabs.create({ url: `${serviceUrl}/profile` });
}));

function messageListener(handler) {
    chrome.runtime.onMessage.addListener(ga.mcatch((req, sender, sendResponse) => {
        console.log('Handling message:', req);
        if (sender.tab.incognito) {
            sendResponse({
                error: {
                    handled: true,
                    authError: 'incognito',
                    message: 'Machete cannot be used in incognito mode',
                }
            });
            return true;
        }

        chrome.pageAction.show(sender.tab.id);
        ga.mga('event', 'background-message', req.action);
        const begin = performance.now();

        co(function*() {
            return yield* handler(req, sender);
        })
        .then(data => {
            const response = { data };
            console.log('Success handling message:', req, "response", response);
            sendResponse(response);
        })
        .catch(error => {
            if (handlePortDisconnected(error, req.action))
                return;
            if (handleMessageTooLong(error, req.action))
                return;

            const response = { status: error.message, error: ga.errorToObject(error) };
            const authError = handleServerErrors(error, req.action);
            if (authError) {
                response.error.handled = true;
                response.error.authError = authError;
                console.warn(error);
            }
            else {
                ga.merror(req, error);
            }

            try {
                sendResponse(response);
            }
            catch (ex) {
                handlePortDisconnected(ex, req.action);
            }
        })
        .then(() => {
            const end = performance.now();
            ga.mga('timing', 'Background Task', req.action, Math.round(end - begin));
        });

        return true;
    }));
}

function* getUser() {
    return yield ajax(`${serviceUrl}/api/user`, {
        method: 'GET',
        dataType: 'json'
    });
}

function startSession(req) {
    return co(dataGather(req));
}

function* dataGather(req) {
    console.log('Data sync start at', moment().format());
    const lastSync = JSON.parse(localStorage.getItem('lastSync')) || {};

    // Store entityIds and domains for further use
    if (req.entityId) {
        addEntityId(req.domain, req.entityId);
    }
    else if (req.domain) {
        addSellerDomain(req.domain);
    }

    /* These requires MUST go here to avoid a circular require */
    const kdp = require('./kdp.js'); // eslint-disable-line global-require
    const seller = require('./seller.js'); // eslint-disable-line global-require
    const sp = require('./sp.js'); // eslint-disable-line global-require

    const oldSync = Math.max(..._.values(lastSync));
    let newSync = 0;
    for (const mod of [sp, seller, kdp]) {
        if (!mod.name) {
            console.error('looking at nameless module', mod);
            throw new Error('module has no name');
        }

        if (lastSync[mod.name]) {
            const moduleSync = moment(lastSync[mod.name]);
            console.log('Last data sync for', mod.name, 'at', moduleSync.format());
            if (moment().isSame(moduleSync, 'day')) {
                console.log(mod.name, 'sync is up-to-date');
                continue;
            }
        }
        else {
            console.log('No recorded sync for', mod.name);
        }

        try {
            console.log('Data sync', mod.name, 'start at', moment().format());
            yield* mod.dataGather(req);
            newSync = Date.now();
            lastSync[mod.name] = newSync;
        }
        catch (ex) {
            if (!handleServerErrors(ex, 'dataGather:'+mod.name))
                ga.mex(ex);
        }
        console.log('Data sync', mod.name, 'finish at', moment().format());
    }

    cache.clear();
    localStorage.setItem('lastSync', JSON.stringify(lastSync));

    console.log('Data sync finish at', moment().format());
    return Math.max(newSync, oldSync);
}

function addEntityId(domain, entityId) {
    if (!domain || !entityId) {
        ga.merror("bad arguments to addEntityId:", JSON.stringify([domain, entityId]));
        return;
    }

    const ids = JSON.parse(localStorage.getItem(entityIdKey)) || [];
    if (!ids.find(x => x.entityId == entityId || x == entityId)) {
        ids.push({ domain, entityId });
        localStorage.setItem(entityIdKey, JSON.stringify(ids));
    }
}

function getEntityIds() {
    const entityIds = JSON.parse(localStorage.getItem(entityIdKey)) || [];

    // TODO: This moves old-style entityIds to the new system. Eventually delete
    // this once all users have been updated.
    const toDelete = [];
    for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key.match(/^campaignData_/)) {
            toDelete.push(key);

            const entityId = key.replace('campaignData_', '');
            if (entityId) {
                entityIds.push({ entityId });
            }
        }
    }
    for (const key of toDelete) {
        localStorage.removeItem(key);
    }

    // Fix up entityIds to ensure that every one has a stored domain
    let ids = entityIds.map(item => {
        if (typeof item == 'string') {
            return { domain: 'ams.amazon.com', entityId: item };
        }
        else if (!item.domain) {
            item.domain = 'ams.amazon.com';
        }
        return item;
    });

    ids = _.uniqBy(ids, 'entityId');
    localStorage.setItem(entityIdKey, JSON.stringify(ids));

    return ids;
}

function addSellerDomain(domain) {
    if (!domain) {
        ga.merror("bad arguments to addEntityId:", JSON.stringify(domain));
        return;
    }

    const ids = JSON.parse(localStorage.getItem(sellerDomainKey)) || [];
    if (!ids.includes(domain)) {
        ids.push(domain);
        localStorage.setItem(sellerDomainKey, JSON.stringify(ids));
    }
}

function getSellerDomains() {
    return JSON.parse(localStorage.getItem(sellerDomainKey)) || [];
}

function setAlarm() {
    chrome.alarms.onAlarm.addListener(ga.mcatch(alarm => {
        if (alarm.name == alarmKey) {
            co(dataGather({})).catch(ga.mex);
        }
    }));
    return ga.mpromise(resolve => {
        chrome.alarms.get(alarmKey, alarm => {
            if (!alarm) {
                const when = Date.now() + 1000;
                chrome.alarms.create(alarmKey, {
                    when,
                    periodInMinutes: alarmPeriodMinutes,
                });
                console.log('set alarm ', alarmKey, 'for', moment(when).format());
                resolve(true);
            }
            resolve(false);
        });
    });
}

function handleServerErrors(ex, desc) {
    if (ex.url && ex.url.match(/machete-app.com/)) {
        if (ex.message.match(/^401/)) {
            ga.mga('event', 'error-handled', 'auth-error-401', desc);
            return 'notLoggedIn';
        }
        if (ex.message.match(/^402/)) {
            ga.mga('event', 'error-handled', 'auth-error-402', desc);
            return 'notAllowed';
        }
        if (ex.message.match(/^403/)) {
            ga.mga('event', 'error-handled', 'auth-error-403', desc);
            return 'notOwned';
        }
    }
    if (ex.message.match(/^404/)) {
        ga.mga('event', 'error-handled', 'network-error-404', desc);
        return 'notFound';
    }
    if (ex.message.match(/^50/)) {
        ga.mga('event', 'error-handled', 'server-error', desc);
        return 'serverError';
    }
    if (ex.message.match(/0 error/)) {
        ga.mga('event', 'error-handled', 'network-error-unknown', desc);
        return 'networkError';
    }
    return null;
}

function handlePortDisconnected(ex, desc) {
    if (ex.message.match(/disconnected port object/)) {
        ga.mga('event', 'error-handled', 'port-disconnected', desc);
        return true;
    }
    return false;
}

function handleMessageTooLong(ex, req) {
    if (ex.message.match(/exceeded maximum allowed length/)) {
        ga.merror(ex.message, req);
        return true;
    }
    return false;
}

function* ajax(url, opts) {
    const init = {
        method: opts.method,
        headers: new Headers(),
        mode: 'cors',
        credentials: 'include',
    };

    if (opts.queryData) {
        const q = Object.keys(opts.queryData).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(opts.queryData[key]));
        url += '?' + q.join('&');
    }

    if (opts.formData) {
        init.body = new FormData();
        for (const key of Object.keys(opts.formData)) {
            init.body.append(key, opts.formData[key]);
        }
        init.headers.set('Content-Type', 'multipart/form-data');
    }
    else if (opts.jsonData) {
        init.body = JSON.stringify(opts.jsonData);
        init.headers.set('Content-Type', 'application/json');
    }

    try {
        const response = yield window.fetch(url, init);
        if (!response.ok) {
            const err = new Error(`${response.status} ${response.statusText}`);
            throw err;
        }

        if (opts.responseType == 'json')
            return yield response.json();
        return yield response.text();
    }
    catch (ex) {
        ex.method = opts.method;
        ex.url = url;
        throw ex;
    }
}

function parallelQueue(items, fn) {
    return new Promise((resolve, reject) => {
        const queue = qu((item, callback) => {
            co(fn(item))
            .then((...results) => callback(null, ...results))
            .catch(callback);
        }, 3);

        queue.drain = resolve;
        queue.error = reject;
        queue.push(items);
    });
}

module.exports = {
    serviceUrl,
    messageListener,
    getUser: cache.coMemo(getUser, { maxAge: 2 * constants.timespan.minute }),
    handleServerErrors,
    ajax,
    parallelQueue,
    cache,
    startSession,
    addEntityId,
    getEntityIds,
    addSellerDomain,
    getSellerDomains,
};
<commit_msg>fixed user to use json response type<commit_after>const qu = require('async/queue');
const co = require('co');
const moment = require('moment');
const _ = require('lodash');

const constants = require('../common/constants.js');
const ga = require('../common/ga.js');
const cache = require('../common/data-cache.js')();
const serviceUrl = `https://${constants.hostname}`;

const alarmPeriodMinutes = 12 * 60;
const alarmKey = 'macheteSync';
const entityIdKey = 'spEntityIds';
const sellerDomainKey = 'sellerDomains';
const lastVersionKey = 'lastVersion';

chrome.runtime.onInstalled.addListener(details => {
    const manifest = chrome.runtime.getManifest();
    if (details.reason == 'install') {
        chrome.tabs.create({ url: `${serviceUrl}/${process.env.PRODUCT}/welcome` });
    }
    localStorage.setItem(lastVersionKey, manifest.version);
    setAlarm();
});

chrome.pageAction.onClicked.addListener(ga.mcatch(() => {
    chrome.tabs.create({ url: `${serviceUrl}/profile` });
}));

function messageListener(handler) {
    chrome.runtime.onMessage.addListener(ga.mcatch((req, sender, sendResponse) => {
        console.log('Handling message:', req);
        if (sender.tab.incognito) {
            sendResponse({
                error: {
                    handled: true,
                    authError: 'incognito',
                    message: 'Machete cannot be used in incognito mode',
                }
            });
            return true;
        }

        chrome.pageAction.show(sender.tab.id);
        ga.mga('event', 'background-message', req.action);
        const begin = performance.now();

        co(function*() {
            return yield* handler(req, sender);
        })
        .then(data => {
            const response = { data };
            console.log('Success handling message:', req, "response", response);
            sendResponse(response);
        })
        .catch(error => {
            if (handlePortDisconnected(error, req.action))
                return;
            if (handleMessageTooLong(error, req.action))
                return;

            const response = { status: error.message, error: ga.errorToObject(error) };
            const authError = handleServerErrors(error, req.action);
            if (authError) {
                response.error.handled = true;
                response.error.authError = authError;
                console.warn(error);
            }
            else {
                ga.merror(req, error);
            }

            try {
                sendResponse(response);
            }
            catch (ex) {
                handlePortDisconnected(ex, req.action);
            }
        })
        .then(() => {
            const end = performance.now();
            ga.mga('timing', 'Background Task', req.action, Math.round(end - begin));
        });

        return true;
    }));
}

function* getUser() {
    return yield ajax(`${serviceUrl}/api/user`, {
        method: 'GET',
        responseType: 'json'
    });
}

function startSession(req) {
    return co(dataGather(req));
}

function* dataGather(req) {
    console.log('Data sync start at', moment().format());
    const lastSync = JSON.parse(localStorage.getItem('lastSync')) || {};

    // Store entityIds and domains for further use
    if (req.entityId) {
        addEntityId(req.domain, req.entityId);
    }
    else if (req.domain) {
        addSellerDomain(req.domain);
    }

    /* These requires MUST go here to avoid a circular require */
    const kdp = require('./kdp.js'); // eslint-disable-line global-require
    const seller = require('./seller.js'); // eslint-disable-line global-require
    const sp = require('./sp.js'); // eslint-disable-line global-require

    const oldSync = Math.max(..._.values(lastSync));
    let newSync = 0;
    for (const mod of [sp, seller, kdp]) {
        if (!mod.name) {
            console.error('looking at nameless module', mod);
            throw new Error('module has no name');
        }

        if (lastSync[mod.name]) {
            const moduleSync = moment(lastSync[mod.name]);
            console.log('Last data sync for', mod.name, 'at', moduleSync.format());
            if (moment().isSame(moduleSync, 'day')) {
                console.log(mod.name, 'sync is up-to-date');
                continue;
            }
        }
        else {
            console.log('No recorded sync for', mod.name);
        }

        try {
            console.log('Data sync', mod.name, 'start at', moment().format());
            yield* mod.dataGather(req);
            newSync = Date.now();
            lastSync[mod.name] = newSync;
        }
        catch (ex) {
            if (!handleServerErrors(ex, 'dataGather:'+mod.name))
                ga.mex(ex);
        }
        console.log('Data sync', mod.name, 'finish at', moment().format());
    }

    cache.clear();
    localStorage.setItem('lastSync', JSON.stringify(lastSync));

    console.log('Data sync finish at', moment().format());
    return Math.max(newSync, oldSync);
}

function addEntityId(domain, entityId) {
    if (!domain || !entityId) {
        ga.merror("bad arguments to addEntityId:", JSON.stringify([domain, entityId]));
        return;
    }

    const ids = JSON.parse(localStorage.getItem(entityIdKey)) || [];
    if (!ids.find(x => x.entityId == entityId || x == entityId)) {
        ids.push({ domain, entityId });
        localStorage.setItem(entityIdKey, JSON.stringify(ids));
    }
}

function getEntityIds() {
    const entityIds = JSON.parse(localStorage.getItem(entityIdKey)) || [];

    // TODO: This moves old-style entityIds to the new system. Eventually delete
    // this once all users have been updated.
    const toDelete = [];
    for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key.match(/^campaignData_/)) {
            toDelete.push(key);

            const entityId = key.replace('campaignData_', '');
            if (entityId) {
                entityIds.push({ entityId });
            }
        }
    }
    for (const key of toDelete) {
        localStorage.removeItem(key);
    }

    // Fix up entityIds to ensure that every one has a stored domain
    let ids = entityIds.map(item => {
        if (typeof item == 'string') {
            return { domain: 'ams.amazon.com', entityId: item };
        }
        else if (!item.domain) {
            item.domain = 'ams.amazon.com';
        }
        return item;
    });

    ids = _.uniqBy(ids, 'entityId');
    localStorage.setItem(entityIdKey, JSON.stringify(ids));

    return ids;
}

function addSellerDomain(domain) {
    if (!domain) {
        ga.merror("bad arguments to addEntityId:", JSON.stringify(domain));
        return;
    }

    const ids = JSON.parse(localStorage.getItem(sellerDomainKey)) || [];
    if (!ids.includes(domain)) {
        ids.push(domain);
        localStorage.setItem(sellerDomainKey, JSON.stringify(ids));
    }
}

function getSellerDomains() {
    return JSON.parse(localStorage.getItem(sellerDomainKey)) || [];
}

function setAlarm() {
    chrome.alarms.onAlarm.addListener(ga.mcatch(alarm => {
        if (alarm.name == alarmKey) {
            co(dataGather({})).catch(ga.mex);
        }
    }));
    return ga.mpromise(resolve => {
        chrome.alarms.get(alarmKey, alarm => {
            if (!alarm) {
                const when = Date.now() + 1000;
                chrome.alarms.create(alarmKey, {
                    when,
                    periodInMinutes: alarmPeriodMinutes,
                });
                console.log('set alarm ', alarmKey, 'for', moment(when).format());
                resolve(true);
            }
            resolve(false);
        });
    });
}

function handleServerErrors(ex, desc) {
    if (ex.url && ex.url.match(/machete-app.com/)) {
        if (ex.message.match(/^401/)) {
            ga.mga('event', 'error-handled', 'auth-error-401', desc);
            return 'notLoggedIn';
        }
        if (ex.message.match(/^402/)) {
            ga.mga('event', 'error-handled', 'auth-error-402', desc);
            return 'notAllowed';
        }
        if (ex.message.match(/^403/)) {
            ga.mga('event', 'error-handled', 'auth-error-403', desc);
            return 'notOwned';
        }
    }
    if (ex.message.match(/^404/)) {
        ga.mga('event', 'error-handled', 'network-error-404', desc);
        return 'notFound';
    }
    if (ex.message.match(/^50/)) {
        ga.mga('event', 'error-handled', 'server-error', desc);
        return 'serverError';
    }
    if (ex.message.match(/0 error/)) {
        ga.mga('event', 'error-handled', 'network-error-unknown', desc);
        return 'networkError';
    }
    return null;
}

function handlePortDisconnected(ex, desc) {
    if (ex.message.match(/disconnected port object/)) {
        ga.mga('event', 'error-handled', 'port-disconnected', desc);
        return true;
    }
    return false;
}

function handleMessageTooLong(ex, req) {
    if (ex.message.match(/exceeded maximum allowed length/)) {
        ga.merror(ex.message, req);
        return true;
    }
    return false;
}

function* ajax(url, opts) {
    const init = {
        method: opts.method,
        headers: new Headers(),
        mode: 'cors',
        credentials: 'include',
    };

    if (opts.queryData) {
        const q = Object.keys(opts.queryData).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(opts.queryData[key]));
        url += '?' + q.join('&');
    }

    if (opts.formData) {
        init.body = new FormData();
        for (const key of Object.keys(opts.formData)) {
            init.body.append(key, opts.formData[key]);
        }
        init.headers.set('Content-Type', 'multipart/form-data');
    }
    else if (opts.jsonData) {
        init.body = JSON.stringify(opts.jsonData);
        init.headers.set('Content-Type', 'application/json');
    }

    try {
        const response = yield window.fetch(url, init);
        if (!response.ok) {
            const err = new Error(`${response.status} ${response.statusText}`);
            throw err;
        }

        if (opts.responseType == 'json')
            return yield response.json();
        return yield response.text();
    }
    catch (ex) {
        ex.method = opts.method;
        ex.url = url;
        throw ex;
    }
}

function parallelQueue(items, fn) {
    return new Promise((resolve, reject) => {
        const queue = qu((item, callback) => {
            co(fn(item))
            .then((...results) => callback(null, ...results))
            .catch(callback);
        }, 3);

        queue.drain = resolve;
        queue.error = reject;
        queue.push(items);
    });
}

module.exports = {
    serviceUrl,
    messageListener,
    getUser: cache.coMemo(getUser, { maxAge: 2 * constants.timespan.minute }),
    handleServerErrors,
    ajax,
    parallelQueue,
    cache,
    startSession,
    addEntityId,
    getEntityIds,
    addSellerDomain,
    getSellerDomains,
};
<|endoftext|>


<commit_before>import Input from '../../mixins/input'
import { debounce } from '../../util/helpers'

export default {
  name: 'select',

  mixins: [Input],

  data () {
    return {
      inputValue: this.multiple ? Array.from(this.value || []) : new Array(this.value || 0),
      inputSearch: this.value ? this.value[this.itemText] : '',
      filteredItems: null,
      menuActive: false,
      appendIconCbPrivate: this.removeAllSelected
    }
  },

  props: {
    items: {
      type: Array,
      default: () => []
    },
    itemText: {
      type: String,
      default: 'text'
    },
    appendIcon: {
      type: String,
      default: 'arrow_drop_down'
    },
    multiple: Boolean,
    autocomplete: Boolean,
    singleLine: Boolean,
    chips: Boolean,
    close: Boolean,
    debounce: {
      type: Number,
      default: 300
    }
  },

  computed: {
    classes () {
      return {
        'input-group--text-field': true,
        'input-group--select': true,
        'input-group--autocomplete': this.autocomplete,
        'input-group--single-line': this.singleLine,
        'input-group--multi-line': this.multiLine
      }
    }
  },

  watch: {
    focused () {
      this.$emit('focused', this.focused)
    },
    value (val) {
      this.inputValue = this.multiple ? val || [] : new Array(val)
    },
    inputValue (val) {
      this.$emit('input', this.multiple ? val : val[0])
    },
    menuActive (isActive) {
      if (!isActive) this.$refs.searchField.blur()
    }
  },

  methods: {
    isDirty () {
      return this.inputValue.length
    },

    focus () {
      this.focused = true
      this.closeAction(true)
    },

    blur () {
      this.$nextTick(() => (this.focused = false))
      this.closeAction(false)
    },

    // TODO: Maybe this should be computed prop?
    filterItems () {
      const { items, inputSearch, itemText } = this

      this.filteredItems = inputSearch && this.autocomplete
        ? items.filter(item => item[itemText].includes(inputSearch))
        : null
    },

    isSelected (item) {
      return this.inputValue.includes(item)
    },

    addSelected (item) {
      if (this.multiple) {
        this.inputValue.push(item)
        this.inputSearch = ''
      } else {
        this.inputValue = [item]
        this.inputSearch = item[this.itemText]
      }
      this.filterItems()
    },

    removeSelected (item) {
      this.inputValue.splice(this.inputValue.findIndex(s => s === item), 1)
    },

    removeAllSelected (e) {
      if (!this.appendIconAlt) return

      e.stopPropagation()
      this.inputValue = []
      this.inputSearch = ''
      this.closeAction(false)
    },

    closeAction (on = true) {
      this.appendIconAlt = on && this.close && this.inputValue.length ? 'close' : ''
    },

    genMenu (h) {
      const data = {
        ref: 'menu',
        style: {
          width: '100%'
        },
        props: {
          offsetY: this.autocomplete,
          auto: !this.autocomplete,
          closeOnClick: !this.multiple,
          value: this.menuActive,
          nudgeBottom: -20,
          nudgeTop: -5
        },
        on: {
          input: (val) => { this.menuActive = val }
        },
        nativeOn: {
          '!mouseenter': () => { this.closeAction() },
          'mouseleave': () => { this.closeAction(false) }
        }
      }

      return h('v-menu', data, [this.genActivator(h), this.genList(h)])
    },

    genActivator (h) {
      const data = { slot: 'activator' }
      return h('div', data, [this.genInputGroup(h, [this.genSelectionsAndSearch(h)])])
    },

    genSelectionsAndSearch (h) {
      const data = {
        slot: 'activator',
        style: { // TODO: Move this to stylus file somewhere.
          'display': 'flex',
          'flex-wrap': 'wrap',
          'width': '100%'
        }
      }

      return this.multiple
        ? h('div', data, this.genSelections(h).concat(this.genSearchField(h)))
        : h('div', data, [this.genSearchField(h)])
    },

    genSelections (h) {
      const chips = this.chips
      const slots = this.$scopedSlots.selection
      const comma = true // <-- default

      return this.inputValue.map(item => {
        if (slots) return this.genSlotSelection(h, item)
        if (chips) return this.genChipSelection(h, item)
        if (comma) return this.genCommaSelection(h, item)
      })
    },

    genSlotSelection (h, item) {
      return this.$scopedSlots.selection({ parent: this, item: item })
    },

    genChipSelection (h, item) {
      const data = {
        props: { close: true },
        on: {
          input: val => { if (val === false) this.removeSelected(item) }
        },
        nativeOn: {
          click: e => { e.stopPropagation() }
        }
      }

      return h('v-chip', data, item[this.itemText])
    },

    genCommaSelection (h, item) {
      const data = {
        style: { // TODO: Move this to stylus file somewhere.
          'font-size': '16px',
          'height': '30px',
          'padding-top': '4px',
          'padding-right': '4px'
        }
      }

      return h('div', data, item[this.itemText] + ',')
    },

    genSearchField (h) {
      const data = {
        ref: 'searchField',
        domProps: {
          value: this.inputSearch
        },
        on: {
          input: e => { this.inputSearch = e.target.value },
          focus: () => { this.focus() },
          blur: () => { this.blur() },
          keyup: debounce(this.filterItems, this.debounce)
        }
      }

      return h('input', data)
    },

    genList (h) {
      const list = (this.filteredItems || this.items).map(item => this.genListItem(h, item))
      return h('v-list', list)
    },

    genListItem (h, item) {
      return h('v-list-item', [this.genTile(h, item)])
    },

    genTile (h, item) {
      const data = {
        'class': {
          'list__tile--active': this.isSelected(item)
        },
        'nativeOn': {
          click: () => {
            this.multiple && this.isSelected(item)
              ? this.removeSelected(item)
              : this.addSelected(item)

            this.$refs.searchField.focus()
          }
        }
      }

      return this.$scopedSlots.item
        ? h('v-list-tile', data, this.$scopedSlots.item({ parent: this, item: item }))
        : h('v-list-tile', data, [this.genAction(h, item), this.genContent(h, item)])
    },

    genAction (h, item) {
      if (!this.multiple) return null

      const checkbox = h('v-checkbox', { props: { 'inputValue': this.isSelected(item) }})
      return h('v-list-tile-action', [checkbox])
    },

    genContent (h, item) {
      return h('v-list-tile-content', [h('v-list-tile-title', item[this.itemText])])
    }
  },

  render (h) {
    return this.genMenu(h)
  }
}
<commit_msg>removed nudge<commit_after>import Input from '../../mixins/input'
import { debounce } from '../../util/helpers'

export default {
  name: 'select',

  mixins: [Input],

  data () {
    return {
      inputValue: this.multiple ? Array.from(this.value || []) : new Array(this.value || 0),
      inputSearch: this.value ? this.value[this.itemText] : '',
      filteredItems: null,
      menuActive: false,
      appendIconCbPrivate: this.removeAllSelected
    }
  },

  props: {
    items: {
      type: Array,
      default: () => []
    },
    itemText: {
      type: String,
      default: 'text'
    },
    appendIcon: {
      type: String,
      default: 'arrow_drop_down'
    },
    multiple: Boolean,
    autocomplete: Boolean,
    singleLine: Boolean,
    chips: Boolean,
    close: Boolean,
    debounce: {
      type: Number,
      default: 300
    }
  },

  computed: {
    classes () {
      return {
        'input-group--text-field': true,
        'input-group--select': true,
        'input-group--autocomplete': this.autocomplete,
        'input-group--single-line': this.singleLine,
        'input-group--multi-line': this.multiLine
      }
    }
  },

  watch: {
    focused () {
      this.$emit('focused', this.focused)
    },
    value (val) {
      this.inputValue = this.multiple ? val || [] : new Array(val)
    },
    inputValue (val) {
      this.$emit('input', this.multiple ? val : val[0])
    },
    menuActive (isActive) {
      if (!isActive) this.$refs.searchField.blur()
    }
  },

  methods: {
    isDirty () {
      return this.inputValue.length
    },

    focus () {
      this.focused = true
      this.closeAction(true)
    },

    blur () {
      this.$nextTick(() => (this.focused = false))
      this.closeAction(false)
    },

    // TODO: Maybe this should be computed prop?
    filterItems () {
      const { items, inputSearch, itemText } = this

      this.filteredItems = inputSearch && this.autocomplete
        ? items.filter(item => item[itemText].includes(inputSearch))
        : null
    },

    isSelected (item) {
      return this.inputValue.includes(item)
    },

    addSelected (item) {
      if (this.multiple) {
        this.inputValue.push(item)
        this.inputSearch = ''
      } else {
        this.inputValue = [item]
        this.inputSearch = item[this.itemText]
      }
      this.filterItems()
    },

    removeSelected (item) {
      this.inputValue.splice(this.inputValue.findIndex(s => s === item), 1)
    },

    removeAllSelected (e) {
      if (!this.appendIconAlt) return

      e.stopPropagation()
      this.inputValue = []
      this.inputSearch = ''
      this.closeAction(false)
    },

    closeAction (on = true) {
      this.appendIconAlt = on && this.close && this.inputValue.length ? 'close' : ''
    },

    genMenu (h) {
      const data = {
        ref: 'menu',
        style: {
          width: '100%'
        },
        props: {
          offsetY: this.autocomplete,
          auto: !this.autocomplete,
          closeOnClick: !this.multiple,
          value: this.menuActive,
          nudgeBottom: 0,
          nudgeTop: 0
        },
        on: {
          input: (val) => { this.menuActive = val }
        },
        nativeOn: {
          '!mouseenter': () => { this.closeAction() },
          'mouseleave': () => { this.closeAction(false) }
        }
      }

      return h('v-menu', data, [this.genActivator(h), this.genList(h)])
    },

    genActivator (h) {
      const data = { slot: 'activator' }
      return h('div', data, [this.genInputGroup(h, [this.genSelectionsAndSearch(h)])])
    },

    genSelectionsAndSearch (h) {
      const data = {
        slot: 'activator',
        style: { // TODO: Move this to stylus file somewhere.
          'display': 'flex',
          'flex-wrap': 'wrap',
          'width': '100%'
        }
      }

      return this.multiple
        ? h('div', data, this.genSelections(h).concat(this.genSearchField(h)))
        : h('div', data, [this.genSearchField(h)])
    },

    genSelections (h) {
      const chips = this.chips
      const slots = this.$scopedSlots.selection
      const comma = true // <-- default

      return this.inputValue.map(item => {
        if (slots) return this.genSlotSelection(h, item)
        if (chips) return this.genChipSelection(h, item)
        if (comma) return this.genCommaSelection(h, item)
      })
    },

    genSlotSelection (h, item) {
      return this.$scopedSlots.selection({ parent: this, item: item })
    },

    genChipSelection (h, item) {
      const data = {
        props: { close: true },
        on: {
          input: val => { if (val === false) this.removeSelected(item) }
        },
        nativeOn: {
          click: e => { e.stopPropagation() }
        }
      }

      return h('v-chip', data, item[this.itemText])
    },

    genCommaSelection (h, item) {
      const data = {
        style: { // TODO: Move this to stylus file somewhere.
          'font-size': '16px',
          'height': '30px',
          'padding-top': '4px',
          'padding-right': '4px'
        }
      }

      return h('div', data, item[this.itemText] + ',')
    },

    genSearchField (h) {
      const data = {
        ref: 'searchField',
        domProps: {
          value: this.inputSearch
        },
        on: {
          input: e => { this.inputSearch = e.target.value },
          focus: () => { this.focus() },
          blur: () => { this.blur() },
          keyup: debounce(this.filterItems, this.debounce)
        }
      }

      return h('input', data)
    },

    genList (h) {
      const list = (this.filteredItems || this.items).map(item => this.genListItem(h, item))
      return h('v-list', list)
    },

    genListItem (h, item) {
      return h('v-list-item', [this.genTile(h, item)])
    },

    genTile (h, item) {
      const data = {
        'class': {
          'list__tile--active': this.isSelected(item)
        },
        'nativeOn': {
          click: () => {
            this.multiple && this.isSelected(item)
              ? this.removeSelected(item)
              : this.addSelected(item)

            this.$refs.searchField.focus()
          }
        }
      }

      return this.$scopedSlots.item
        ? h('v-list-tile', data, this.$scopedSlots.item({ parent: this, item: item }))
        : h('v-list-tile', data, [this.genAction(h, item), this.genContent(h, item)])
    },

    genAction (h, item) {
      if (!this.multiple) return null

      const checkbox = h('v-checkbox', { props: { 'inputValue': this.isSelected(item) }})
      return h('v-list-tile-action', [checkbox])
    },

    genContent (h, item) {
      return h('v-list-tile-content', [h('v-list-tile-title', item[this.itemText])])
    }
  },

  render (h) {
    return this.genMenu(h)
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import Icon from 'mastodon/components/icon';

export default class ColumnBackButtonSlim extends React.PureComponent {

  static contextTypes = {
    router: PropTypes.object,
  };

  handleClick = (event) => {
    // if history is exhausted, or we would leave mastodon, just go to root.
    if (window.history.state) {
      const state = this.context.router.history.location.state;
      if (event.shiftKey && state && state.mastodonBackSteps) {
        this.context.router.history.go(-state.mastodonBackSteps);
      } else {
        this.context.router.history.goBack();
      }
    } else {
      this.context.router.history.push('/');
    }
  }

  render () {
    return (

          <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
          <FormattedMessage id='column_back_button.label' defaultMessage='Back' />

    );
  }

}
<commit_msg>Fix using wrong component in ColumnBackButtonSlim<commit_after>import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import Icon from 'flavours/glitch/components/icon';

export default class ColumnBackButtonSlim extends React.PureComponent {

  static contextTypes = {
    router: PropTypes.object,
  };

  handleClick = (event) => {
    // if history is exhausted, or we would leave mastodon, just go to root.
    if (window.history.state) {
      const state = this.context.router.history.location.state;
      if (event.shiftKey && state && state.mastodonBackSteps) {
        this.context.router.history.go(-state.mastodonBackSteps);
      } else {
        this.context.router.history.goBack();
      }
    } else {
      this.context.router.history.push('/');
    }
  }

  render () {
    return (

          <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
          <FormattedMessage id='column_back_button.label' defaultMessage='Back' />

    );
  }

}
<|endoftext|>


<commit_before>// Options: --free-variable-checker --require --validate
/*global module require*/

module.exports = (function() {
  "use strict";

  const {def} = require('../../src/sesshim.es6');
  const {bnf} = require('../../src/bootbnf.es6');

  const binary = (left,rights) => rights.reduce((prev,[op,right]) => [op,prev,right], left);

  const qunpack = (h,ms,t) => {
    const result = [h];
    if (ms.length === 1) {
      const [[m,pairs]] = ms;
      result.push(m);
      for (let [q,e] of pairs) {
        result.push(q,e);
      }
    }
    result.push(t);
    return result;
  };

  const {FAIL, Packratter} = require('../../src/scanner.es6');
  // Packratter._debug = true;

  // Whereas SES is a maximal ocap-secure subset of ES6 (EcmaScript
  // 2015), microses is a minimal "better parts"
  // (http://www.infoq.com/presentations/efficient-programming-language-es6)
  // subset of SES-strict. SES is a semantic, not a syntactic, subset
  // of ES6. Microses is mostly defined as a syntactic subset of
  // SES-strict. The following microses grammar is based on
  // http://www.ecma-international.org/ecma-262/6.0/#sec-grammar-summary
  // Unlike that page, lexical productions are named in all upper
  // case. The intention is that microses be a true subset of SES in
  // the sense that every valid microses program is a valid SES
  // program of the same meaning.

  // Unlike ES6 and SES, microses has no semicolon insertion, and so
  // does not need a parser able to handle that. However, microses
  // must impose the NO_NEWLINE constraints from ES6, so that every
  // non-rejected microses program is accepted as the same SES
  // program. NO_NEWLINE is a lexical-level placeholder that must
  // never consume anything. It should fail if the whitespace to skip
  // over contains a newline. TODO: Currently this placeholder always
  // succeeds.

  // Microses excludes the RegularExpressionLiteral, instead including
  // the RegExp.make https://github.com/mikesamuel/regexp-make-js
  // template string tag. By omitting this and automatic semicolon
  // insertion, our lexical grammar avoids the context dependence that
  // plague JavaScript lexers.

  // In microses, all reserved words are unconditionally reserved. By
  // contrast, in ES6 and SES, "yield", "await", "implements", etc are
  // conditionally reserved. Thus we avoid the need for parameterized
  // lexical-level productions.

  // Microses excludes both the "in" expression and the for/in loop,
  // and thus avoid the need for parameterized parser-level
  // productions.

  // QUASI_* are lexical-level placeholders. QUASI_ALL should match a
  // self-contained template literal string that has no holes " `...`
  // ". QUASI_HEAD should match the initial literal part of a template
  // literal with holes " `...${ ". QUASI_MID should match the middle
  // " }...${ ", and QUASI_TAIL the end " }...` ". The reason these
  // are difficult is that a "}" during a hole only terminates the
  // hole if it is balanced.  TODO: All these placeholders currently
  // fail.

  // Ouside the lexical grammar, other differences from
  // http://www.ecma-international.org/ecma-262/6.0/#sec-grammar-summary
  // are noted as comments within the grammar below.
  // That page uses a cover grammar do avoid unbounded
  // lookahead. Because the grammar here is defined using a PEG
  // (parsing expression grammar) which supports unbounded lookahead,
  // we avoid the need for a cover grammar.

  // Microses array literals exclude elision (i.e., nothing between
  // commas).

  // Beyond subsetting ES6, this grammar also includes the infix "!"
  // (eventually) operator from
  // http://research.google.com/pubs/pub40673.html

  const microses = bnf`
    # TODO: module syntax
    start ::= body EOF                                     ${(b,_) => (..._) => ['script', b]};

    NO_NEWLINE ::= ;

    QUASI_ALL ::= ${() => FAIL};
    QUASI_HEAD ::= ${() => FAIL};
    QUASI_MID ::= ${() => FAIL};
    QUASI_TAIL ::= ${() => FAIL};

    # Exclude "arguments" and "eval" from IDENT in microses.
    RESERVED_WORD ::=
      KEYWORD / ES6_ONLY_KEYWORD / FUTURE_RESERVED_WORD
    / "arguments" / "eval";

    KEYWORD ::=
      "false" / "true"
    / "break" / "case" / "catch" / "const"
    / "debugger" / "default" / "delete"
    / "else" / "export" / "finally"
    / "for" / "if" / "import"
    / "return" / "switch" / "throw" / "try"
    / "typeof" / "void" / "while";

    # We enumerate these anyway, in order to exclude them from the
    # IDENT token.
    ES6_ONLY_KEYWORD ::=
      "null" / "class" / "continue" / "do" / "extends"
    / "function" / "in" / "instanceof" / "new" / "super"
    / "this" / "var" / "with" / "yield";

    FUTURE_RESERVED_WORD ::=
      "enum" / "await"
    / "implements" / "interface" / "package"
    / "private" / "protected" / "public";

    # Microses primaryExpr does not include "this", ClassExpression,
    # GeneratorExpression, or RegularExpressionLiteral.
    # No "null". No "function" functions.
    primaryExpr ::=
      (NUMBER / STRING / "true" / "false")                 ${n => ['data',JSON.parse(n)]}
    / "[" arg ** "," "]"                                   ${(_,es,_2) => ['array',es]}
    / "{" prop ** "," "}"                                  ${(_,ps,_2) => ['object',ps]}
    / quasiExpr
    / "(" expr ")"                                         ${(_,e,_2) => e}
    / IDENT
    / HOLE                                                 ${h => ['exprHole',h]};

    pattern ::=
      (NUMBER / STRING / "true" / "false")                 ${n => ['matchData',JSON.parse(n)]}
    / "[" param ** "," "]"                                 ${(_,ps,_2) => ['matchArray',ps]}
    / "{" propParam ** "," "}"                             ${(_,ps,_2) => ['matchObj',ps]}
    / IDENT
    / HOLE                                                 ${h => ['patternHole',h]};

    arg ::=
      "..." expr                                           ${(_,e) => ['spread',e]}
    / expr;

    param ::=
      "..." pattern                                        ${(_,p) => ['rest',p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optional',id,e]}
    / pattern;

    # No method definition.
    prop ::=
      "..." expr                                           ${(_,e) => ['spreadObj',e]}
    / key ":" expr                                         ${(k,_,e) => ['prop',k,e]}
    / IDENT                                                ${id => ['prop',id,id]};

    propParam ::=
      "..." pattern                                        ${(_,p) => ['restObj',p]}
    / key ":" pattern                                      ${(k,_,p) => ['matchProp',k,p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optionalProp',id,id,e]}
    / IDENT                                                ${id => ['matchProp',id,id]};

    key ::=
      IDENT / RESERVED_WORD / STRING / NUMBER
    / "[" expr "]"                                         ${(_,k) => ['computed', k]};

    quasiExpr ::=
      QUASI_ALL                                            ${q => ['quasi',[q]]}
    / QUASI_HEAD (expr (QUASI_MID expr)*)? QUASI_TAIL      ${(h,ms,t) => ['quasi',qunpack(h,ms,t)]};

    later ::= NO_NEWLINE "!";

    # No "new", "super", or MetaProperty. Without "new" we don't need
    # separate MemberExpr and CallExpr productions.
    postExpr ::= primaryExpr postOp*                       ${binary};
    postOp ::=
      "." IDENT                                            ${(_,id) => ['get',id]}
    / "[" expr "]"                                         ${(_,e,_2) => ['index',e]}
    / "(" arg ** "," ")"                                   ${(_,args,_2) => ['call',args]}
    / quasiExpr                                            ${q => ['tag',q]}

    / later IDENT                                          ${(_,id) => ['getLater',id]}
    / later "[" expr "]"                                   ${(_,_2,e,_3) => ['indexLater',e]}
    / later "(" arg ** "," ")"                             ${(_,_2,args,_3) => ['callLater',args]}
    / later quasiExpr                                      ${(_,q) => ['tagLater',q]};

    preExpr ::=
      "delete" fieldExpr                                   ${(_,fe) => ['delete', fe]}
    / preOp preExpr                                        ${(op,e) => [op,e]}
    / postExpr;

    # No prefix or postfix "++" or "--".
    preOp ::= "void" / "typeof" / "+" / "-" / "!";

    # No bitwise operators, "instanceof", or "in".  Unlike ES6, none
    # of the relational operators associate. To help readers, mixing
    # relational operators always requires explicit parens.
    multExpr ::= preExpr (("*" / "/" / "%") preExpr)*      ${binary};
    addExpr ::= multExpr (("+" / "-") multExpr)*           ${binary};
    relExpr ::= addExpr (relOp addExpr)?                   ${binary};
    relOp ::= "<" / ">" / "<=" / ">=" / "===" / "!==";
    andThenExpr ::= relExpr ("&&" relExpr)*                ${binary};
    orElseExpr ::= andThenExpr ("||" andThenExpr)*         ${binary};

    # No trinary ("?:") expression
    # No comma expression, so assignment expression is expr.
    expr ::=
      lValue assignOp expr                                 ${(lv,op,rv) => [op,lv,rv]}
    / arrow
    / orElseExpr;

    # lValue is divided into IDENT and fieldExpr because microses
    # syntactically disallows "delete" IDENT.
    # No pseudo-pattern lValues.
    lValue ::= IDENT / fieldExpr;

    fieldExpr ::=
      primaryExpr "." IDENT                                ${(pe,_,id) => ['get',pe,id]}
    / primaryExpr "[" expr "]"                             ${(pe,_,e,_2) => ['index',pe,e]}
    / primaryExpr later IDENT                              ${(pe,_,id) => ['getLater',pe,id]}
    / primaryExpr later "[" expr "]"                       ${(pe,_,_2,e,_3) => ['indexLater',pe,e]};

    # No bitwise operators
    assignOp ::= "=" / "*=" / "/=" / "%=" / "+=" / "-=";

    arrow ::=
      params NO_NEWLINE "=>" block                         ${(ps,_,_2,b) => ['arrow',ps,b]}
    / params NO_NEWLINE "=>" expr                          ${(ps,_,_2,e) => ['lambda',ps,e]};
    params ::=
      IDENT                                                ${id => [id]}
    / "(" param ** "," ")"                                 ${(_,ps,_2) => ps};

    # No "var", empty statement, "continue", "with", "do/while",
    # "for/in", or labelled statement. None of the insane variations
    # of "for". Only blocks are accepted for flow-of-control
    # statements.
    statement ::=
      block
    / "if" "(" expr ")" block "else" block                 ${(_,_2,c,_3,t,_4,e) => ['if',c,t,e]}
    / "if" "(" expr ")" block                              ${(_,_2,c,_3,t) => ['if',c,t]}
    / "for" "(" declaration expr? ";" expr? ")" block      ${(_,_2,d,c,_3,i,_4,b) => ['for',d,c,i,b]}
    / "for" "(" declOp binding "of" expr ")" block         ${(_,_2,d,_3,e,_4,b) => ['forOf',d,e,b]}
    / "while" "(" expr ")" block                           ${(_,_2,c,_3,b) => ['while',c,b]}
    / "try" block catcher finalizer                        ${(_,b,c,f) => ['try',b,c,f]}
    / "try" block finalizer                                ${(_,b,f) => ['try',b,f]}
    / "try" block catcher                                  ${(_,b,c) => ['try',b,c]}
    / "switch" "(" expr ")" "{" branch* "}"                ${(_,_2,e,_3,_4,bs,_5) => ['switch',e,bs]}
    / terminator
    / "debugger" ";"                                       ${(_,_2) => ['debugger']}
    / expr ";"                                             ${(e,_) => e};

    # Each case branch must end in a terminating statement. No
    # labelled break.
    terminator ::=
      "return" NO_NEWLINE expr ";"                         ${(_,_2,e,_3) => ['return',e]}
    / "return" ";"                                         ${(_,_2) => ['return']}
    / "break" ";"                                          ${(_,_2) => ['break']}
    / "throw" expr ";"                                     ${(_,e,_2) => ['throw',e]};

    # no "function", generator, or "class" declaration.
    declaration ::= declOp binding ** "," ";"              ${(op,decls,_) => [op, decls]};
    declOp ::= "const" / "let";
    # Initializer is mandatory
    binding ::= pattern "=" expr                           ${(p,_,e) => ['bind', p, e]};

    catcher ::= "catch" "(" pattern ")" block              ${(_,_2,p,_3,b) => ['catch',p,b]};
    finalizer ::= "finally" block                          ${(_,b) => ['finally',b]};

    branch ::= caseLabel+ "{" body terminator "}"          ${(cs,_,b,t,_2) => ['branch',cs,[...b,t]]};
    caseLabel ::=
      "case" expr ":"                                      ${(_,e) => ['case', e]}
    / "default" ":"                                        ${(_,_2) => ['default']};

    block ::= "{" body "}"                                 ${(_,b,_2) => ['block', b]};
    body ::= (statement / declaration)*;
  `;

  return def({microses});
}());
<commit_msg>Update microses.es6<commit_after>// Options: --free-variable-checker --require --validate
/*global module require*/

module.exports = (function() {
  "use strict";

  const {def} = require('../../src/sesshim.es6');
  const {bnf} = require('../../src/bootbnf.es6');

  const binary = (left,rights) => rights.reduce((prev,[op,right]) => [op,prev,right], left);

  const qunpack = (h,ms,t) => {
    const result = [h];
    if (ms.length === 1) {
      const [[m,pairs]] = ms;
      result.push(m);
      for (let [q,e] of pairs) {
        result.push(q,e);
      }
    }
    result.push(t);
    return result;
  };

  const {FAIL, Packratter} = require('../../src/scanner.es6');
  // Packratter._debug = true;

  // Whereas SES is a maximal ocap-secure subset of ES6 (EcmaScript
  // 2015), microses is a minimal "better parts"
  // (http://www.infoq.com/presentations/efficient-programming-language-es6)
  // subset of SES-strict. SES is a semantic, not a syntactic, subset
  // of ES6. Microses is mostly defined as a syntactic subset of
  // SES-strict. The following microses grammar is based on
  // http://www.ecma-international.org/ecma-262/6.0/#sec-grammar-summary
  // Unlike that page, lexical productions are named in all upper
  // case. The intention is that microses be a true subset of SES in
  // the sense that every valid microses program is a valid SES
  // program of the same meaning.

  // Unlike ES6 and SES, microses has no semicolon insertion, and so
  // does not need a parser able to handle that. However, microses
  // must impose the NO_NEWLINE constraints from ES6, so that every
  // non-rejected microses program is accepted as the same SES
  // program. NO_NEWLINE is a lexical-level placeholder that must
  // never consume anything. It should fail if the whitespace to skip
  // over contains a newline. TODO: Currently this placeholder always
  // succeeds.

  // Microses excludes the RegularExpressionLiteral, instead including
  // the RegExp.make https://github.com/mikesamuel/regexp-make-js
  // template string tag. By omitting this and automatic semicolon
  // insertion, our lexical grammar avoids the context dependence that
  // plague JavaScript lexers.

  // In microses, all reserved words are unconditionally reserved. By
  // contrast, in ES6 and SES, "yield", "await", "implements", etc are
  // conditionally reserved. Thus we avoid the need for parameterized
  // lexical-level productions.

  // Microses excludes both the "in" expression and the for/in loop,
  // and thus avoid the need for parameterized parser-level
  // productions.

  // QUASI_* are lexical-level placeholders. QUASI_ALL should match a
  // self-contained template literal string that has no holes " `...`
  // ". QUASI_HEAD should match the initial literal part of a template
  // literal with holes " `...${ ". QUASI_MID should match the middle
  // " }...${ ", and QUASI_TAIL the end " }...` ". The reason these
  // are difficult is that a "}" during a hole only terminates the
  // hole if it is balanced.  TODO: All these placeholders currently
  // fail.

  // Ouside the lexical grammar, other differences from
  // http://www.ecma-international.org/ecma-262/6.0/#sec-grammar-summary
  // are noted as comments within the grammar below.
  // That page uses a cover grammar to avoid unbounded
  // lookahead. Because the grammar here is defined using a PEG
  // (parsing expression grammar) which supports unbounded lookahead,
  // we avoid the need for a cover grammar.

  // Microses array literals exclude elision (i.e., nothing between
  // commas).

  // Microses omits "arguments" and "eval".
  // EcmaScript/strict already limits "arguments" and "eval"
  // to the point that they are effectively keywords.
  // Microses does include "..." both as rest and spread
  // which provides the useful functionality of "arguments"
  // with less confusion.
  // The EcmaScript/strict "eval" can be used for both
  // direct and indirect eval. Microses has no direct eval
  // and can use other evaluator APIs to make up the difference.

  // Beyond subsetting ES6, this grammar also includes the infix "!"
  // (eventually) operator from
  // http://research.google.com/pubs/pub40673.html

  const microses = bnf`
    # TODO: module syntax
    start ::= body EOF                                     ${(b,_) => (..._) => ['script', b]};

    NO_NEWLINE ::= ;

    QUASI_ALL ::= ${() => FAIL};
    QUASI_HEAD ::= ${() => FAIL};
    QUASI_MID ::= ${() => FAIL};
    QUASI_TAIL ::= ${() => FAIL};

    # Exclude "arguments" and "eval" from IDENT in microses.
    RESERVED_WORD ::=
      KEYWORD / ES6_ONLY_KEYWORD / FUTURE_RESERVED_WORD
    / "arguments" / "eval";

    KEYWORD ::=
      "null" / "false" / "true"
    / "break" / "case" / "catch" / "const"
    / "debugger" / "default" / "delete"
    / "else" / "export" / "finally"
    / "for" / "if" / "import"
    / "return" / "switch" / "throw" / "try"
    / "typeof" / "void" / "while";

    # Microses omits these ES6 keywords.
    # We enumerate these anyway, in order to exclude them from the
    # IDENT token.
    ES6_ONLY_KEYWORD ::=
      "class" / "continue" / "do" / "extends"
    / "function" / "in" / "instanceof" / "new" / "super"
    / "this" / "var" / "with" / "yield";

    FUTURE_RESERVED_WORD ::=
      "enum" / "await"
    / "implements" / "interface" / "package"
    / "private" / "protected" / "public";

    dataLiteral ::= NUMBER / STRING / "null" / "true" / "false";

    # Microses primaryExpr does not include "this", ClassExpression,
    # GeneratorExpression, or RegularExpressionLiteral.
    # No "function" functions.
    primaryExpr ::=
      IDENT
    / dataLiteral                                          ${n => ['data',JSON.parse(n)]}
    / "[" arg ** "," "]"                                   ${(_,es,_2) => ['array',es]}
    / "{" prop ** "," "}"                                  ${(_,ps,_2) => ['object',ps]}
    / quasiExpr
    / "(" expr ")"                                         ${(_,e,_2) => e}
    / HOLE                                                 ${h => ['exprHole',h]};

    pattern ::=
      IDENT
    / dataLiteral                                          ${n => ['matchData',JSON.parse(n)]}
    / "[" param ** "," "]"                                 ${(_,ps,_2) => ['matchArray',ps]}
    / "{" propParam ** "," "}"                             ${(_,ps,_2) => ['matchObj',ps]}
    / HOLE                                                 ${h => ['patternHole',h]};

    arg ::=
      "..." expr                                           ${(_,e) => ['spread',e]}
    / expr;

    param ::=
      "..." pattern                                        ${(_,p) => ['rest',p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optional',id,e]}
    / pattern;

    # No method definition.
    prop ::=
      "..." expr                                           ${(_,e) => ['spreadObj',e]}
    / key ":" expr                                         ${(k,_,e) => ['prop',k,e]}
    / IDENT                                                ${id => ['prop',id,id]};

    propParam ::=
      "..." pattern                                        ${(_,p) => ['restObj',p]}
    / key ":" pattern                                      ${(k,_,p) => ['matchProp',k,p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optionalProp',id,id,e]}
    / IDENT                                                ${id => ['matchProp',id,id]};

    key ::=
      IDENT / RESERVED_WORD / STRING / NUMBER
    / "[" expr "]"                                         ${(_,k) => ['computed', k]};

    quasiExpr ::=
      QUASI_ALL                                            ${q => ['quasi',[q]]}
    / QUASI_HEAD (expr (QUASI_MID expr)*)? QUASI_TAIL      ${(h,ms,t) => ['quasi',qunpack(h,ms,t)]};

    later ::= NO_NEWLINE "!";

    # No "new", "super", or MetaProperty. Without "new" we don't need
    # separate MemberExpr and CallExpr productions.
    postExpr ::= primaryExpr postOp*                       ${binary};
    postOp ::=
      "." IDENT                                            ${(_,id) => ['get',id]}
    / "[" expr "]"                                         ${(_,e,_2) => ['index',e]}
    / "(" arg ** "," ")"                                   ${(_,args,_2) => ['call',args]}
    / quasiExpr                                            ${q => ['tag',q]}

    / later IDENT                                          ${(_,id) => ['getLater',id]}
    / later "[" expr "]"                                   ${(_,_2,e,_3) => ['indexLater',e]}
    / later "(" arg ** "," ")"                             ${(_,_2,args,_3) => ['callLater',args]}
    / later quasiExpr                                      ${(_,q) => ['tagLater',q]};

    preExpr ::=
      "delete" fieldExpr                                   ${(_,fe) => ['delete', fe]}
    / preOp preExpr                                        ${(op,e) => [op,e]}
    / postExpr;

    # No prefix or postfix "++" or "--".
    preOp ::= "void" / "typeof" / "+" / "-" / "!";

    # No bitwise operators, "instanceof", or "in".  Unlike ES6, none
    # of the relational operators associate. To help readers, mixing
    # relational operators always requires explicit parens.
    multExpr ::= preExpr (("*" / "/" / "%") preExpr)*      ${binary};
    addExpr ::= multExpr (("+" / "-") multExpr)*           ${binary};
    relExpr ::= addExpr (relOp addExpr)?                   ${binary};
    relOp ::= "<" / ">" / "<=" / ">=" / "===" / "!==";
    andThenExpr ::= relExpr ("&&" relExpr)*                ${binary};
    orElseExpr ::= andThenExpr ("||" andThenExpr)*         ${binary};

    # No trinary ("?:") expression
    # No comma expression, so assignment expression is expr.
    expr ::=
      lValue assignOp expr                                 ${(lv,op,rv) => [op,lv,rv]}
    / arrow
    / orElseExpr;

    # lValue is divided into IDENT and fieldExpr because microses
    # syntactically disallows "delete" IDENT.
    # No pseudo-pattern lValues.
    lValue ::= IDENT / fieldExpr;

    fieldExpr ::=
      primaryExpr "." IDENT                                ${(pe,_,id) => ['get',pe,id]}
    / primaryExpr "[" expr "]"                             ${(pe,_,e,_2) => ['index',pe,e]}
    / primaryExpr later IDENT                              ${(pe,_,id) => ['getLater',pe,id]}
    / primaryExpr later "[" expr "]"                       ${(pe,_,_2,e,_3) => ['indexLater',pe,e]};

    # No bitwise operators
    assignOp ::= "=" / "*=" / "/=" / "%=" / "+=" / "-=";

    arrow ::=
      params NO_NEWLINE "=>" block                         ${(ps,_,_2,b) => ['arrow',ps,b]}
    / params NO_NEWLINE "=>" expr                          ${(ps,_,_2,e) => ['lambda',ps,e]};
    params ::=
      IDENT                                                ${id => [id]}
    / "(" param ** "," ")"                                 ${(_,ps,_2) => ps};

    # No "var", empty statement, "continue", "with",
    # "for/in", or labelled statement.
    # None of the insane variations of "for".
    # Only blocks are accepted for flow-of-control statements.
    statement ::=
      block
    / "if" "(" expr ")" block "else" block                 ${(_,_2,c,_3,t,_4,e) => ['if',c,t,e]}
    / "if" "(" expr ")" block                              ${(_,_2,c,_3,t) => ['if',c,t]}
    / "do" block "while" "(" expr ")" ";"                  ${(_,b,_2,_3,c,_4,_5) => ['doWhile',b,c]}
    / "while" "(" expr ")" block                           ${(_,_2,c,_3,b) => ['while',c,b]}
    / "for" "(" declaration expr? ";" expr? ")" block      ${(_,_2,d,c,_3,i,_4,b) => ['for',d,c,i,b]}
    / "for" "(" declOp binding "of" expr ")" block         ${(_,_2,d,_3,e,_4,b) => ['forOf',d,e,b]}
    / "try" block catcher finalizer                        ${(_,b,c,f) => ['try',b,c,f]}
    / "try" block finalizer                                ${(_,b,f) => ['try',b,f]}
    / "try" block catcher                                  ${(_,b,c) => ['try',b,c]}
    / "switch" "(" expr ")" "{" branch* "}"                ${(_,_2,e,_3,_4,bs,_5) => ['switch',e,bs]}
    / terminator
    / "debugger" ";"                                       ${(_,_2) => ['debugger']}
    / expr ";"                                             ${(e,_) => e};

    # Each case branch must end in a terminating statement. No
    # labelled break.
    terminator ::=
      "return" NO_NEWLINE expr ";"                         ${(_,_2,e,_3) => ['return',e]}
    / "return" ";"                                         ${(_,_2) => ['return']}
    / "break" ";"                                          ${(_,_2) => ['break']}
    / "throw" expr ";"                                     ${(_,e,_2) => ['throw',e]};

    # no "function", generator, or "class" declaration.
    declaration ::= declOp binding ** "," ";"              ${(op,decls,_) => [op, decls]};
    declOp ::= "const" / "let";
    # Initializer is mandatory
    binding ::= pattern "=" expr                           ${(p,_,e) => ['bind', p, e]};

    catcher ::= "catch" "(" pattern ")" block              ${(_,_2,p,_3,b) => ['catch',p,b]};
    finalizer ::= "finally" block                          ${(_,b) => ['finally',b]};

    branch ::= caseLabel+ "{" body terminator "}"          ${(cs,_,b,t,_2) => ['branch',cs,[...b,t]]};
    caseLabel ::=
      "case" expr ":"                                      ${(_,e) => ['case', e]}
    / "default" ":"                                        ${(_,_2) => ['default']};

    block ::= "{" body "}"                                 ${(_,b,_2) => ['block', b]};
    body ::= (statement / declaration)*;
  `;

  return def({microses});
}());
<|endoftext|>


<commit_before>"use strict";

function EducationViewModel (data) {

    var self = this;

    self.type = "EducationViewModel";
    self.rootvm = data.rootvm;

    self.parentvm = data.parentvm;

    self.location_uuid = ko.observable();
    self.initialize = function(){
        if(self.location_uuid()){
            self.parentvm.location_uuid(self.location_uuid());
        }
    };

    self.reading_indicator_titles =  ['rpass50', '_rpass50',
        'rpass10', '_rpass10',
        'rpass15', '_rpass15',
        'rpass20', '_rpass20',
        'rpass41', '_rpass41'];

    self.math_indicator_titles =  ['mpass50', '_mpass50',
        'mpass10', '_mpass10',
        'mpass15', '_mpass15',
        'mpass20', '_mpass20',
        'mpass41', '_mpass41'];

    self.kindergarten_readiness_titles =  ['band1', '_band1',
        'band2', '_band2',
        'band3', '_band3',
        'meanscore'];

    self.other_indicators = ['qslots', '_attend']

    self.overview_indicators = ['rpass50', 'mpass50'];

    self.indicator_titles = (self.reading_indicator_titles.concat(
        self.math_indicator_titles)).concat(self.kindergarten_readiness_titles).concat(
        self.other_indicators);

    self.indicators = ko.observableArray([]);

    self.parentvm.selected_location.subscribe(function(){
        self.parentvm.look_up_indicator_and_values(self.indicator_titles,
            self.look_up_indicator_complete);
    });

    self.csv_link =  ko.computed(function(){

        var base_url= "/api/indicator-categories-with-values-by-location-csv";
        base_url += '?location_uuid=' + self.location_uuid();
        base_url += '&page_title=Education';

        for(var i = 0; i<self.indicator_titles.length; i++)
        {
            base_url += '&indicators[]=' + self.indicator_titles[i];
        };
        return base_url;
    });

    self.observable_timestamps = ko.observableArray([]);

    self.look_up_indicator_complete = function(data){

        self.observable_timestamps(ko.utils.arrayMap(
            data.distinct_observable_timestamps || [],
            function(x){
                return new moment(x.observation_timestamp);
            }
        ));

        self.indicators(ko.utils.arrayMap(
            data.indicator_values || [],
            function (x) {
                x.indicator.rootvm = self.rootvm;
                x.indicator.indicator_values = x.indicator_values;

                var i = new Indicator(x.indicator);

                if (['meanscore', 'qslots'].indexOf(i.title()) >= 0){
                    i.percent_change_available(false);
                }
                return i;
            }));

    };
};
<commit_msg>added a few more not applicables<commit_after>"use strict";

function EducationViewModel (data) {

    var self = this;

    self.type = "EducationViewModel";
    self.rootvm = data.rootvm;

    self.parentvm = data.parentvm;

    self.location_uuid = ko.observable();
    self.initialize = function(){
        if(self.location_uuid()){
            self.parentvm.location_uuid(self.location_uuid());
        }
    };

    self.reading_indicator_titles =  ['rpass50', '_rpass50',
        'rpass10', '_rpass10',
        'rpass15', '_rpass15',
        'rpass20', '_rpass20',
        'rpass41', '_rpass41'];

    self.math_indicator_titles =  ['mpass50', '_mpass50',
        'mpass10', '_mpass10',
        'mpass15', '_mpass15',
        'mpass20', '_mpass20',
        'mpass41', '_mpass41'];

    self.kindergarten_readiness_titles =  ['band1', '_band1',
        'band2', '_band2',
        'band3', '_band3',
        'meanscore'];

    self.other_indicators = ['qslots', '_attend']

    self.overview_indicators = ['rpass50', 'mpass50'];

    self.indicator_titles = (self.reading_indicator_titles.concat(
        self.math_indicator_titles)).concat(self.kindergarten_readiness_titles).concat(
        self.other_indicators);

    self.indicators = ko.observableArray([]);

    self.parentvm.selected_location.subscribe(function(){
        self.parentvm.look_up_indicator_and_values(self.indicator_titles,
            self.look_up_indicator_complete);
    });

    self.csv_link =  ko.computed(function(){

        var base_url= "/api/indicator-categories-with-values-by-location-csv";
        base_url += '?location_uuid=' + self.location_uuid();
        base_url += '&page_title=Education';

        for(var i = 0; i<self.indicator_titles.length; i++)
        {
            base_url += '&indicators[]=' + self.indicator_titles[i];
        };
        return base_url;
    });

    self.observable_timestamps = ko.observableArray([]);

    self.look_up_indicator_complete = function(data){

        self.observable_timestamps(ko.utils.arrayMap(
            data.distinct_observable_timestamps || [],
            function(x){
                return new moment(x.observation_timestamp);
            }
        ));

        self.indicators(ko.utils.arrayMap(
            data.indicator_values || [],
            function (x) {
                x.indicator.rootvm = self.rootvm;
                x.indicator.indicator_values = x.indicator_values;

                var i = new Indicator(x.indicator);

                if (['meanscore', 'qslots', 'band1', '_band1', 'band2', '_band2', 'band3', '_band3'].indexOf(i.title()) >= 0){
                    i.percent_change_available(false);
                }
                return i;
            }));

    };
};
<|endoftext|>


<commit_before>import { withPluginApi, decorateCooked } from "discourse/lib/plugin-api";
import { Tag } from "discourse/lib/to-markdown";
import ComposerController from "discourse/controllers/composer";

function spoil($elem) {
  $(".spoiler", $elem)
    .removeClass("spoiler")
    .addClass("spoiled")
    .spoil();
}

function initializeSpoiler(api) {
  api.decorateCooked(spoil);

  api.addToolbarPopupMenuOptionsCallback(() => {
    return {
      action: "insertSpoiler",
      icon: "magic",
      label: "spoiler.title"
    };
  });

  ComposerController.reopen({
    actions: {
      insertSpoiler() {
        this.get("toolbarEvent").applySurround(
          "[spoiler]",
          "[/spoiler]",
          "spoiler_text",
          { multiline: false }
        );
      }
    }
  });

  if (Tag) {
    Tag.prototype.decorate = function(text) {
      const attr = this.element.attributes;
      if (attr.class === "spoiled") {
        this.prefix = "[spoiler]";
        this.suffix = "[/spoiler]";
      }

      if (this.prefix || this.suffix) {
        text = [this.prefix, text, this.suffix].join("");
      }

      if (this.inline) {
        text = " " + text + " ";
      }

      return text;
    };

    Tag.block = function(name, prefix, suffix) {
      return class extends Tag {
        constructor() {
          super(name, prefix, suffix);
          this.gap = "\n\n";
        }

        decorate(text) {
          const attr = this.element.attributes;
          if (this.name === "div" && attr.class === "spoiled") {
            this.prefix = "[spoiler]";
            this.suffix = "[/spoiler]";
            text = text.trim();
          }

          return `${this.gap}${this.prefix}${text}${this.suffix}${this.gap}`;
        }
      };
    };
  }
}

export default {
  name: "apply-spoilers",
  initialize(container) {
    const siteSettings = container.lookup("site-settings:main");
    if (siteSettings.spoiler_enabled) {
      withPluginApi("0.5", initializeSpoiler, {
        noApi: () => decorateCooked(container, spoil)
      });
    }
  }
};
<commit_msg>FIX: Use tight list format for GDocs html to markdown<commit_after>import { withPluginApi, decorateCooked } from "discourse/lib/plugin-api";
import { Tag } from "discourse/lib/to-markdown";
import ComposerController from "discourse/controllers/composer";

function spoil($elem) {
  $(".spoiler", $elem)
    .removeClass("spoiler")
    .addClass("spoiled")
    .spoil();
}

function initializeSpoiler(api) {
  api.decorateCooked(spoil);

  api.addToolbarPopupMenuOptionsCallback(() => {
    return {
      action: "insertSpoiler",
      icon: "magic",
      label: "spoiler.title"
    };
  });

  ComposerController.reopen({
    actions: {
      insertSpoiler() {
        this.get("toolbarEvent").applySurround(
          "[spoiler]",
          "[/spoiler]",
          "spoiler_text",
          { multiline: false }
        );
      }
    }
  });

  if (Tag) {
    Tag.prototype.decorate = function(text) {
      const attr = this.element.attributes;
      if (attr.class === "spoiled") {
        this.prefix = "[spoiler]";
        this.suffix = "[/spoiler]";
      }

      if (this.prefix || this.suffix) {
        text = [this.prefix, text, this.suffix].join("");
      }

      if (this.inline) {
        text = " " + text + " ";
      }

      return text;
    };

    Tag.block = function(name, prefix, suffix) {
      return class extends Tag {
        constructor() {
          super(name, prefix, suffix);
          this.gap = "\n\n";
        }

        decorate(text) {
          const attr = this.element.attributes;
          const parent = this.element.parent;

          if (this.name === "p" && parent && parent.name === "li") {
            // fix for google docs
            this.gap = "";
          }

          if (this.name === "div" && attr.class === "spoiled") {
            this.prefix = "[spoiler]";
            this.suffix = "[/spoiler]";
            text = text.trim();
          }

          return `${this.gap}${this.prefix}${text}${this.suffix}${this.gap}`;
        }
      };
    };
  }
}

export default {
  name: "apply-spoilers",
  initialize(container) {
    const siteSettings = container.lookup("site-settings:main");
    if (siteSettings.spoiler_enabled) {
      withPluginApi("0.5", initializeSpoiler, {
        noApi: () => decorateCooked(container, spoil)
      });
    }
  }
};
<|endoftext|>


<commit_before>/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var d3 = require('d3');

var Registry = require('../../registry');
var Lib = require('../../lib');
var Drawing = require('../drawing');
var Color = require('../color');

var subTypes = require('../../traces/scatter/subtypes');
var stylePie = require('../../traces/pie/style_one');

module.exports = function style(s, gd) {
    s.each(function(d) {
        var traceGroup = d3.select(this);

        var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
        layers.style('opacity', d[0].trace.opacity);

        var fill = layers
            .selectAll('g.legendfill')
                .data([d]);
        fill.enter().append('g')
            .classed('legendfill', true);

        var line = layers
            .selectAll('g.legendlines')
                .data([d]);
        line.enter().append('g')
            .classed('legendlines', true);

        var symbol = layers
            .selectAll('g.legendsymbols')
                .data([d]);
        symbol.enter().append('g')
            .classed('legendsymbols', true);

        symbol.selectAll('g.legendpoints')
            .data([d])
          .enter().append('g')
            .classed('legendpoints', true);
    })
    .each(styleBars)
    .each(styleBoxes)
    .each(stylePies)
    .each(styleLines)
    .each(stylePoints)
    .each(styleCandles)
    .each(styleOHLC);

    function styleLines(d) {
        var trace = d[0].trace;
        var showFill = trace.visible && trace.fill && trace.fill !== 'none';
        var showLine = subTypes.hasLines(trace);
        var contours = trace.contours;

        if(contours && contours.type === 'constraint') {
            showLine = contours.showlines;
            showFill = contours._operation !== '=';
        }

        var fill = d3.select(this).select('.legendfill').selectAll('path')
            .data(showFill ? [d] : []);
        fill.enter().append('path').classed('js-fill', true);
        fill.exit().remove();
        fill.attr('d', 'M5,0h30v6h-30z')
            .call(Drawing.fillGroupStyle);

        var line = d3.select(this).select('.legendlines').selectAll('path')
            .data(showLine ? [d] : []);
        line.enter().append('path').classed('js-line', true)
            .attr('d', 'M5,0h30');
        line.exit().remove();
        line.call(Drawing.lineGroupStyle);
    }

    function stylePoints(d) {
        var d0 = d[0];
        var trace = d0.trace;
        var showMarkers = subTypes.hasMarkers(trace);
        var showText = subTypes.hasText(trace);
        var showLines = subTypes.hasLines(trace);
        var dMod, tMod;

        // 'scatter3d' don't use gd.calcdata,
        // use d0.trace to infer arrayOk attributes

        function boundVal(attrIn, arrayToValFn, bounds) {
            var valIn = Lib.nestedProperty(trace, attrIn).get();
            var valToBound = (Array.isArray(valIn) && arrayToValFn) ?
                arrayToValFn(valIn) :
                valIn;

            if(bounds) {
                if(valToBound < bounds[0]) return bounds[0];
                else if(valToBound > bounds[1]) return bounds[1];
            }
            return valToBound;
        }

        function pickFirst(array) { return array[0]; }

        // constrain text, markers, etc so they'll fit on the legend
        if(showMarkers || showText || showLines) {
            var dEdit = {};
            var tEdit = {};

            if(showMarkers) {
                dEdit.mc = boundVal('marker.color', pickFirst);
                dEdit.mx = boundVal('marker.symbol', pickFirst);
                dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
                dEdit.ms = boundVal('marker.size', Lib.mean, [2, 16]);
                dEdit.mlc = boundVal('marker.line.color', pickFirst);
                dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5]);
                tEdit.marker = {
                    sizeref: 1,
                    sizemin: 1,
                    sizemode: 'diameter'
                };
            }

            if(showLines) {
                tEdit.line = {
                    width: boundVal('line.width', pickFirst, [0, 10])
                };
            }

            if(showText) {
                dEdit.tx = 'Aa';
                dEdit.tp = boundVal('textposition', pickFirst);
                dEdit.ts = 10;
                dEdit.tc = boundVal('textfont.color', pickFirst);
                dEdit.tf = boundVal('textfont.family', pickFirst);
            }

            dMod = [Lib.minExtend(d0, dEdit)];
            tMod = Lib.minExtend(trace, tEdit);

            // always show legend items in base state
            tMod.selectedpoints = null;
        }

        var ptgroup = d3.select(this).select('g.legendpoints');

        var pts = ptgroup.selectAll('path.scatterpts')
            .data(showMarkers ? dMod : []);
        pts.enter().append('path').classed('scatterpts', true)
            .attr('transform', 'translate(20,0)');
        pts.exit().remove();
        pts.call(Drawing.pointStyle, tMod, gd);

        // 'mrc' is set in pointStyle and used in textPointStyle:
        // constrain it here
        if(showMarkers) dMod[0].mrc = 3;

        var txt = ptgroup.selectAll('g.pointtext')
            .data(showText ? dMod : []);
        txt.enter()
            .append('g').classed('pointtext', true)
                .append('text').attr('transform', 'translate(20,0)');
        txt.exit().remove();
        txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
    }

    function styleBars(d) {
        var trace = d[0].trace,
            marker = trace.marker || {},
            markerLine = marker.line || {},
            barpath = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendbar')
                .data(Registry.traceIs(trace, 'bar') ? [d] : []);
        barpath.enter().append('path').classed('legendbar', true)
            .attr('d', 'M6,6H-6V-6H6Z')
            .attr('transform', 'translate(20,0)');
        barpath.exit().remove();
        barpath.each(function(d) {
            var p = d3.select(this),
                d0 = d[0],
                w = (d0.mlw + 1 || markerLine.width + 1) - 1;

            p.style('stroke-width', w + 'px')
                .call(Color.fill, d0.mc || marker.color);

            if(w) {
                p.call(Color.stroke, d0.mlc || markerLine.color);
            }
        });
    }

    function styleBoxes(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendbox')
                .data(Registry.traceIs(trace, 'box-violin') && trace.visible ? [d] : []);
        pts.enter().append('path').classed('legendbox', true)
            // if we want the median bar, prepend M6,0H-6
            .attr('d', 'M6,6H-6V-6H6Z')
            .attr('transform', 'translate(20,0)');
        pts.exit().remove();
        pts.each(function() {
            var w = trace.line.width,
                p = d3.select(this);

            p.style('stroke-width', w + 'px')
                .call(Color.fill, trace.fillcolor);

            if(w) {
                Color.stroke(p, trace.line.color);
            }
        });
    }

    function styleCandles(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendcandle')
                .data(trace.type === 'candlestick' && trace.visible ? [d, d] : []);
        pts.enter().append('path').classed('legendcandle', true)
            .attr('d', function(_, i) {
                if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
                return 'M15,0H8M8,-6V6H-8Z'; // decreasing
            })
            .attr('transform', 'translate(20,0)')
            .style('stroke-miterlimit', 1);
        pts.exit().remove();
        pts.each(function(_, i) {
            var container = trace[i ? 'increasing' : 'decreasing'];
            var w = container.line.width,
                p = d3.select(this);

            p.style('stroke-width', w + 'px')
                .call(Color.fill, container.fillcolor);

            if(w) {
                Color.stroke(p, container.line.color);
            }
        });
    }

    function styleOHLC(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendohlc')
                .data(trace.type === 'ohlc' && trace.visible ? [d, d] : []);
        pts.enter().append('path').classed('legendohlc', true)
            .attr('d', function(_, i) {
                if(i) return 'M-15,0H0M-8,-6V0'; // increasing
                return 'M15,0H0M8,6V0'; // decreasing
            })
            .attr('transform', 'translate(20,0)')
            .style('stroke-miterlimit', 1);
        pts.exit().remove();
        pts.each(function(_, i) {
            var container = trace[i ? 'increasing' : 'decreasing'];
            var w = container.line.width,
                p = d3.select(this);

            p.style('fill', 'none')
                .call(Drawing.dashLine, container.line.dash, w);

            if(w) {
                Color.stroke(p, container.line.color);
            }
        });
    }

    function stylePies(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendpie')
                .data(Registry.traceIs(trace, 'pie') && trace.visible ? [d] : []);
        pts.enter().append('path').classed('legendpie', true)
            .attr('d', 'M6,6H-6V-6H6Z')
            .attr('transform', 'translate(20,0)');
        pts.exit().remove();

        if(pts.size()) pts.call(stylePie, d[0], trace);
    }
};
<commit_msg>bound scalar marker.size in legenditems<commit_after>/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var d3 = require('d3');

var Registry = require('../../registry');
var Lib = require('../../lib');
var Drawing = require('../drawing');
var Color = require('../color');

var subTypes = require('../../traces/scatter/subtypes');
var stylePie = require('../../traces/pie/style_one');

module.exports = function style(s, gd) {
    s.each(function(d) {
        var traceGroup = d3.select(this);

        var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
        layers.style('opacity', d[0].trace.opacity);

        var fill = layers
            .selectAll('g.legendfill')
                .data([d]);
        fill.enter().append('g')
            .classed('legendfill', true);

        var line = layers
            .selectAll('g.legendlines')
                .data([d]);
        line.enter().append('g')
            .classed('legendlines', true);

        var symbol = layers
            .selectAll('g.legendsymbols')
                .data([d]);
        symbol.enter().append('g')
            .classed('legendsymbols', true);

        symbol.selectAll('g.legendpoints')
            .data([d])
          .enter().append('g')
            .classed('legendpoints', true);
    })
    .each(styleBars)
    .each(styleBoxes)
    .each(stylePies)
    .each(styleLines)
    .each(stylePoints)
    .each(styleCandles)
    .each(styleOHLC);

    function styleLines(d) {
        var trace = d[0].trace;
        var showFill = trace.visible && trace.fill && trace.fill !== 'none';
        var showLine = subTypes.hasLines(trace);
        var contours = trace.contours;

        if(contours && contours.type === 'constraint') {
            showLine = contours.showlines;
            showFill = contours._operation !== '=';
        }

        var fill = d3.select(this).select('.legendfill').selectAll('path')
            .data(showFill ? [d] : []);
        fill.enter().append('path').classed('js-fill', true);
        fill.exit().remove();
        fill.attr('d', 'M5,0h30v6h-30z')
            .call(Drawing.fillGroupStyle);

        var line = d3.select(this).select('.legendlines').selectAll('path')
            .data(showLine ? [d] : []);
        line.enter().append('path').classed('js-line', true)
            .attr('d', 'M5,0h30');
        line.exit().remove();
        line.call(Drawing.lineGroupStyle);
    }

    function stylePoints(d) {
        var d0 = d[0];
        var trace = d0.trace;
        var showMarkers = subTypes.hasMarkers(trace);
        var showText = subTypes.hasText(trace);
        var showLines = subTypes.hasLines(trace);
        var dMod, tMod;

        // 'scatter3d' don't use gd.calcdata,
        // use d0.trace to infer arrayOk attributes

        function boundVal(attrIn, arrayToValFn, bounds) {
            var valIn = Lib.nestedProperty(trace, attrIn).get();
            var valToBound = (Array.isArray(valIn) && arrayToValFn) ?
                arrayToValFn(valIn) :
                valIn;

            if(bounds) {
                if(valToBound < bounds[0]) return bounds[0];
                else if(valToBound > bounds[1]) return bounds[1];
            }
            return valToBound;
        }

        function pickFirst(array) { return array[0]; }

        // constrain text, markers, etc so they'll fit on the legend
        if(showMarkers || showText || showLines) {
            var dEdit = {};
            var tEdit = {};

            if(showMarkers) {
                dEdit.mc = boundVal('marker.color', pickFirst);
                dEdit.mx = boundVal('marker.symbol', pickFirst);
                dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
                dEdit.mlc = boundVal('marker.line.color', pickFirst);
                dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5]);
                tEdit.marker = {
                    sizeref: 1,
                    sizemin: 1,
                    sizemode: 'diameter'
                };

                var ms = boundVal('marker.size', Lib.mean, [2, 16]);
                dEdit.ms = ms;
                tEdit.marker.size = ms;
            }

            if(showLines) {
                tEdit.line = {
                    width: boundVal('line.width', pickFirst, [0, 10])
                };
            }

            if(showText) {
                dEdit.tx = 'Aa';
                dEdit.tp = boundVal('textposition', pickFirst);
                dEdit.ts = 10;
                dEdit.tc = boundVal('textfont.color', pickFirst);
                dEdit.tf = boundVal('textfont.family', pickFirst);
            }

            dMod = [Lib.minExtend(d0, dEdit)];
            tMod = Lib.minExtend(trace, tEdit);

            // always show legend items in base state
            tMod.selectedpoints = null;
        }

        var ptgroup = d3.select(this).select('g.legendpoints');

        var pts = ptgroup.selectAll('path.scatterpts')
            .data(showMarkers ? dMod : []);
        pts.enter().append('path').classed('scatterpts', true)
            .attr('transform', 'translate(20,0)');
        pts.exit().remove();
        pts.call(Drawing.pointStyle, tMod, gd);

        // 'mrc' is set in pointStyle and used in textPointStyle:
        // constrain it here
        if(showMarkers) dMod[0].mrc = 3;

        var txt = ptgroup.selectAll('g.pointtext')
            .data(showText ? dMod : []);
        txt.enter()
            .append('g').classed('pointtext', true)
                .append('text').attr('transform', 'translate(20,0)');
        txt.exit().remove();
        txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
    }

    function styleBars(d) {
        var trace = d[0].trace,
            marker = trace.marker || {},
            markerLine = marker.line || {},
            barpath = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendbar')
                .data(Registry.traceIs(trace, 'bar') ? [d] : []);
        barpath.enter().append('path').classed('legendbar', true)
            .attr('d', 'M6,6H-6V-6H6Z')
            .attr('transform', 'translate(20,0)');
        barpath.exit().remove();
        barpath.each(function(d) {
            var p = d3.select(this),
                d0 = d[0],
                w = (d0.mlw + 1 || markerLine.width + 1) - 1;

            p.style('stroke-width', w + 'px')
                .call(Color.fill, d0.mc || marker.color);

            if(w) {
                p.call(Color.stroke, d0.mlc || markerLine.color);
            }
        });
    }

    function styleBoxes(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendbox')
                .data(Registry.traceIs(trace, 'box-violin') && trace.visible ? [d] : []);
        pts.enter().append('path').classed('legendbox', true)
            // if we want the median bar, prepend M6,0H-6
            .attr('d', 'M6,6H-6V-6H6Z')
            .attr('transform', 'translate(20,0)');
        pts.exit().remove();
        pts.each(function() {
            var w = trace.line.width,
                p = d3.select(this);

            p.style('stroke-width', w + 'px')
                .call(Color.fill, trace.fillcolor);

            if(w) {
                Color.stroke(p, trace.line.color);
            }
        });
    }

    function styleCandles(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendcandle')
                .data(trace.type === 'candlestick' && trace.visible ? [d, d] : []);
        pts.enter().append('path').classed('legendcandle', true)
            .attr('d', function(_, i) {
                if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
                return 'M15,0H8M8,-6V6H-8Z'; // decreasing
            })
            .attr('transform', 'translate(20,0)')
            .style('stroke-miterlimit', 1);
        pts.exit().remove();
        pts.each(function(_, i) {
            var container = trace[i ? 'increasing' : 'decreasing'];
            var w = container.line.width,
                p = d3.select(this);

            p.style('stroke-width', w + 'px')
                .call(Color.fill, container.fillcolor);

            if(w) {
                Color.stroke(p, container.line.color);
            }
        });
    }

    function styleOHLC(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendohlc')
                .data(trace.type === 'ohlc' && trace.visible ? [d, d] : []);
        pts.enter().append('path').classed('legendohlc', true)
            .attr('d', function(_, i) {
                if(i) return 'M-15,0H0M-8,-6V0'; // increasing
                return 'M15,0H0M8,6V0'; // decreasing
            })
            .attr('transform', 'translate(20,0)')
            .style('stroke-miterlimit', 1);
        pts.exit().remove();
        pts.each(function(_, i) {
            var container = trace[i ? 'increasing' : 'decreasing'];
            var w = container.line.width,
                p = d3.select(this);

            p.style('fill', 'none')
                .call(Drawing.dashLine, container.line.dash, w);

            if(w) {
                Color.stroke(p, container.line.color);
            }
        });
    }

    function stylePies(d) {
        var trace = d[0].trace,
            pts = d3.select(this).select('g.legendpoints')
                .selectAll('path.legendpie')
                .data(Registry.traceIs(trace, 'pie') && trace.visible ? [d] : []);
        pts.enter().append('path').classed('legendpie', true)
            .attr('d', 'M6,6H-6V-6H6Z')
            .attr('transform', 'translate(20,0)');
        pts.exit().remove();

        if(pts.size()) pts.call(stylePie, d[0], trace);
    }
};
<|endoftext|>