<commit_before>var collection = require("mongo-col"),
    pd         = require("pd");

var Helpers = {
  // checks if a request is authorized
  checkIfAuthorized: function(req, res) {
    if (req.session.is_logged) {
      return true;
    } else {
      this.messages.addMessage(req, "error", "You need to be logged in to access this page.");
      return res.redirect("/");
    }
  },

  // is a GET request?
  isGet: function(req) {
    if (req.route.method === "get") {
      return true;
    } else {
      return false;
    }
  },

  // is a POST request?
  isPost: function(req) {
    if (req.route.method === "post") {
      return true;
    } else {
      return false;
    }
  },

  // is a new MongoDB object?
  isNew: function(object) {
    if (object._id === undefined || object._id === "") {
      return true;
    } else {
      return false;
    }
  },

  // is the object defined?
  isDefined: function(object) {
    if (object === undefined) {
      return false;
    } else {
      return true;
    }
  },

  slugify: function(str) {
    var from, i, l, to;
    str = str.replace(/^\s+|\s+$/g, "");
    str = str.toLowerCase();
    from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;";
    to = "aaaaeeeeiiiioooouuuunc------";
    i = 0;
    l = from.length;
    while (i < l) {
      str = str.replace(new RegExp(from.charAt(i), "g"), to.charAt(i));
      i++;
    }
    str = str.replace(/[^a-z0-9 -]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
    return str;
  },

  // is the request an AJAX call?
  // To recognize this, we unfortunately cannot rely on the
  // X_REQUESTED_WITH header since some browsers (IE7) don't support it.
  // So we're checking the "Accept" header of the request and see
  // if it's asking for the "application/json" content-type.
  isXHR: function(req, res) {
    if (req.header.accepts('application/json')) {
      return true;
    } else {
      return false;
    }
  }
};

module.exports = Helpers;
<commit_msg>Fixed isXHR to act the same as Django<commit_after>var collection = require("mongo-col"),
    pd         = require("pd");

var Helpers = {
  // checks if a request is authorized
  checkIfAuthorized: function(req, res) {
    if (req.session.is_logged) {
      return true;
    } else {
      this.messages.addMessage(req, "error", "You need to be logged in to access this page.");
      return res.redirect("/");
    }
  },

  // is a GET request?
  isGet: function(req) {
    if (req.route.method === "get") {
      return true;
    } else {
      return false;
    }
  },

  // is a POST request?
  isPost: function(req) {
    if (req.route.method === "post") {
      return true;
    } else {
      return false;
    }
  },

  // is a new MongoDB object?
  isNew: function(object) {
    if (object._id === undefined || object._id === "") {
      return true;
    } else {
      return false;
    }
  },

  // is the object defined?
  isDefined: function(object) {
    if (object === undefined) {
      return false;
    } else {
      return true;
    }
  },

  slugify: function(str) {
    var from, i, l, to;
    str = str.replace(/^\s+|\s+$/g, "");
    str = str.toLowerCase();
    from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;";
    to = "aaaaeeeeiiiioooouuuunc------";
    i = 0;
    l = from.length;
    while (i < l) {
      str = str.replace(new RegExp(from.charAt(i), "g"), to.charAt(i));
      i++;
    }
    str = str.replace(/[^a-z0-9 -]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
    return str;
  },

  // is the request an AJAX call?
  isXHR: function(req, res) {
    if (req.header('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest') {
      return true;
    } else {
      return false;
    }
  }
};

module.exports = Helpers;

<|endoftext|>


<commit_before>import { event as d3_event, select as d3_select } from 'd3-selection';

import { t } from '../core/localizer';

import { actionAddMidpoint } from '../actions/add_midpoint';
import { actionDeleteRelation } from '../actions/delete_relation';
import { actionMove } from '../actions/move';

import { behaviorBreathe } from '../behavior/breathe';
import { behaviorHover } from '../behavior/hover';
import { behaviorLasso } from '../behavior/lasso';
import { behaviorPaste } from '../behavior/paste';
import { behaviorSelect } from '../behavior/select';

import { operationMove } from '../operations/move';

import { geoExtent, geoChooseEdge } from '../geo';
import { modeBrowse } from './browse';
import { modeDragNode } from './drag_node';
import { modeDragNote } from './drag_note';
import { osmNode, osmWay } from '../osm';
import * as Operations from '../operations/index';
import { uiCmd } from '../ui/cmd';
import {
    utilArrayIntersection, utilDeepMemberSelector, utilEntityOrDeepMemberSelector,
    utilEntitySelector, utilKeybinding
} from '../util';

var _relatedParent;

export function modeSelect(context, selectedIDs) {
    var mode = {
        id: 'select',
        button: 'browse'
    };

    var keybinding = utilKeybinding('select');

    var _breatheBehavior = behaviorBreathe(context);
    var _modeDragNode = modeDragNode(context);
    var _selectBehavior;
    var _behaviors = [];

    var _operations = [];
    var _newFeature = false;
    var _follow = false;

    function singular() {
        if (selectedIDs && selectedIDs.length === 1) {
            return context.hasEntity(selectedIDs[0]);
        }
    }

    function selectedEntities() {
        return selectedIDs.map(function(id) {
            return context.hasEntity(id);
        }).filter(Boolean);
    }

    function checkSelectedIDs() {
        var ids = [];
        if (Array.isArray(selectedIDs)) {
            ids = selectedIDs.filter(function(id) {
                return context.hasEntity(id);
            });
        }

        if (!ids.length) {
            context.enter(modeBrowse(context));
            return false;
        } else if ((selectedIDs.length > 1 && ids.length === 1) ||
            (selectedIDs.length === 1 && ids.length > 1)) {
            // switch between single- and multi-select UI
            context.enter(modeSelect(context, ids));
            return false;
        }

        selectedIDs = ids;
        return true;
    }

    // find the common parent ways for nextVertex, previousVertex
    function commonParents() {
        var graph = context.graph();
        var commonParents = [];

        for (var i = 0; i < selectedIDs.length; i++) {
            var entity = context.hasEntity(selectedIDs[i]);
            if (!entity || entity.geometry(graph) !== 'vertex') {
                return [];  // selection includes some not vertexes
            }

            var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
            if (!commonParents.length) {
                commonParents = currParents;
                continue;
            }

            commonParents = utilArrayIntersection(commonParents, currParents);
            if (!commonParents.length) {
                return [];
            }
        }

        return commonParents;
    }

    function singularParent() {
        var parents = commonParents();
        if (!parents || parents.length === 0) {
            _relatedParent = null;
            return null;
        }

        // relatedParent is used when we visit a vertex with multiple
        // parents, and we want to remember which parent line we started on.

        if (parents.length === 1) {
            _relatedParent = parents[0];  // remember this parent for later
            return _relatedParent;
        }

        if (parents.indexOf(_relatedParent) !== -1) {
            return _relatedParent;   // prefer the previously seen parent
        }

        return parents[0];
    }

    mode.selectedIDs = function(val) {
        if (!arguments.length) return selectedIDs;
        selectedIDs = val;
        return mode;
    };

    mode.zoomToSelected = function() {
        context.map().zoomToEase(selectedEntities());
    };

    mode.newFeature = function(val) {
        if (!arguments.length) return _newFeature;
        _newFeature = val;
        return mode;
    };

    mode.selectBehavior = function(val) {
        if (!arguments.length) return _selectBehavior;
        _selectBehavior = val;
        return mode;
    };

    mode.follow = function(val) {
        if (!arguments.length) return _follow;
        _follow = val;
        return mode;
    };

    function loadOperations() {

        _operations.forEach(function(operation) {
            if (operation.behavior) {
                context.uninstall(operation.behavior);
            }
        });

        _operations = Object.values(Operations)
            .map(function(o) { return o(context, selectedIDs); })
            .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade'; });

        var downgradeOperation = Operations.operationDowngrade(context, selectedIDs);
        // don't allow delete if downgrade is available
        var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : Operations.operationDelete(context, selectedIDs);

        _operations.push(lastOperation);

        _operations.forEach(function(operation) {
            if (operation.behavior) {
                context.install(operation.behavior);
            }
        });

        // remove any displayed menu
        context.ui().closeEditMenu();
    }

    mode.operations = function() {
        return _operations;
    };

    mode.enter = function() {
        if (!checkSelectedIDs()) return;

        context.features().forceVisible(selectedIDs);

        _modeDragNode.restoreSelectedIDs(selectedIDs);

        loadOperations();

        if (!_behaviors.length) {
            if (!_selectBehavior) _selectBehavior = behaviorSelect(context);

            _behaviors = [
                behaviorPaste(context),
                _breatheBehavior,
                behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
                _selectBehavior,
                behaviorLasso(context),
                _modeDragNode.behavior,
                modeDragNote(context).behavior
            ];
        }
        _behaviors.forEach(context.install);

        keybinding
            .on(t('inspector.zoom_to.key'), mode.zoomToSelected)
            .on(['[', 'pgup'], previousVertex)
            .on([']', 'pgdown'], nextVertex)
            .on(['{', uiCmd('⌘['), 'home'], firstVertex)
            .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
            .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
            .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
            .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
            .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
            .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
            .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
            .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
            .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
            .on(['\\', 'pause'], nextParent)
            .on('⎋', esc, true);

        d3_select(document)
            .call(keybinding);

        context.ui().sidebar
            .select(selectedIDs, _newFeature);

        context.history()
            .on('change.select', function() {
                loadOperations();
                // reselect after change in case relation members were removed or added
                selectElements();
            })
            .on('undone.select', checkSelectedIDs)
            .on('redone.select', checkSelectedIDs);

        context.map()
            .on('drawn.select', function(info) {
                if (info.full !== false) {
                    selectElements();
                }
            })
            .on('crossEditableZoom.select', function() {
                selectElements();
                _breatheBehavior.restartIfNeeded(context.surface());
            });

        context.map().doubleUpHandler()
            .on('doubleUp.modeSelect', didDoubleUp);

        selectElements();

        if (_follow) {
            var extent = geoExtent();
            var graph = context.graph();
            selectedIDs.forEach(function(id) {
                var entity = context.entity(id);
                extent._extend(entity.extent(graph));
            });

            var loc = extent.center();
            context.map().centerEase(loc);
        }

        function nudgeSelection(delta) {
            return function() {
                // prevent nudging during low zoom selection
                if (!context.map().withinEditableZoom()) return;

                var moveOp = operationMove(context, selectedIDs);
                if (moveOp.disabled()) {
                    context.ui().flash
                        .duration(4000)
                        .iconName('#iD-operation-' + moveOp.id)
                        .iconClass('operation disabled')
                        .text(moveOp.tooltip)();
                } else {
                    context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
                }
            };
        }

        function didDoubleUp(loc) {
            if (!context.map().withinEditableZoom()) return;

            var target = d3_select(d3_event.target);

            var datum = target.datum();
            var entity = datum && datum.properties && datum.properties.entity;
            if (!entity) return;

            if (entity instanceof osmWay && target.classed('target')) {
                var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
                var prev = entity.nodes[choice.index - 1];
                var next = entity.nodes[choice.index];

                context.perform(
                    actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
                    t('operations.add.annotation.vertex')
                );

            } else if (entity.type === 'midpoint') {
                context.perform(
                    actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
                    t('operations.add.annotation.vertex'));
            }
        }

        function selectElements() {
            if (!checkSelectedIDs()) return;

            var surface = context.surface();

            surface.selectAll('.selected-member')
                .classed('selected-member', false);

            surface.selectAll('.selected')
                .classed('selected', false);

            surface.selectAll('.related')
                .classed('related', false);

            if (_relatedParent) {
                surface.selectAll(utilEntitySelector([_relatedParent]))
                    .classed('related', true);
            }

            if (context.map().withinEditableZoom()) {
                // Apply selection styling if not in wide selection

                surface
                    .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
                    .classed('selected-member', true);
                surface
                    .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
                    .classed('selected', true);
            }

        }

        function esc() {
            if (context.container().select('.combobox').size()) return;
            context.enter(modeBrowse(context));
        }

        function firstVertex() {
            d3_event.preventDefault();
            var entity = singular();
            var parent = singularParent();
            var way;

            if (entity && entity.type === 'way') {
                way = entity;
            } else if (parent) {
                way = context.entity(parent);
            }

            if (way) {
                context.enter(
                    modeSelect(context, [way.first()]).follow(true)
                );
            }
        }

        function lastVertex() {
            d3_event.preventDefault();
            var entity = singular();
            var parent = singularParent();
            var way;

            if (entity && entity.type === 'way') {
                way = entity;
            } else if (parent) {
                way = context.entity(parent);
            }

            if (way) {
                context.enter(
                    modeSelect(context, [way.last()]).follow(true)
                );
            }
        }

        function previousVertex() {
            d3_event.preventDefault();
            var parent = singularParent();
            if (!parent) return;

            var way = context.entity(parent);
            var length = way.nodes.length;
            var curr = way.nodes.indexOf(selectedIDs[0]);
            var index = -1;

            if (curr > 0) {
                index = curr - 1;
            } else if (way.isClosed()) {
                index = length - 2;
            }

            if (index !== -1) {
                context.enter(
                    modeSelect(context, [way.nodes[index]]).follow(true)
                );
            }
        }

        function nextVertex() {
            d3_event.preventDefault();
            var parent = singularParent();
            if (!parent) return;

            var way = context.entity(parent);
            var length = way.nodes.length;
            var curr = way.nodes.indexOf(selectedIDs[0]);
            var index = -1;

            if (curr < length - 1) {
                index = curr + 1;
            } else if (way.isClosed()) {
                index = 0;
            }

            if (index !== -1) {
                context.enter(
                    modeSelect(context, [way.nodes[index]]).follow(true)
                );
            }
        }

        function nextParent() {
            d3_event.preventDefault();
            var parents = commonParents();
            if (!parents || parents.length < 2) return;

            var index = parents.indexOf(_relatedParent);
            if (index < 0 || index > parents.length - 2) {
                _relatedParent = parents[0];
            } else {
                _relatedParent = parents[index + 1];
            }

            var surface = context.surface();
            surface.selectAll('.related')
                .classed('related', false);

            if (_relatedParent) {
                surface.selectAll(utilEntitySelector([_relatedParent]))
                    .classed('related', true);
            }
        }
    };

    mode.exit = function() {

        _operations.forEach(function(operation) {
            if (operation.behavior) {
                context.uninstall(operation.behavior);
            }
        });
        _operations = [];

        _behaviors.forEach(context.uninstall);

        d3_select(document)
            .call(keybinding.unbind);

        context.ui().closeEditMenu();

        context.history()
            .on('change.select', null)
            .on('undone.select', null)
            .on('redone.select', null);

        var surface = context.surface();

        surface
            .selectAll('.selected-member')
            .classed('selected-member', false);

        surface
            .selectAll('.selected')
            .classed('selected', false);

        surface
            .selectAll('.highlighted')
            .classed('highlighted', false);

        surface
            .selectAll('.related')
            .classed('related', false);

        context.map().on('drawn.select', null);
        context.ui().sidebar.hide();
        context.features().forceVisible([]);

        var entity = singular();
        if (_newFeature && entity && entity.type === 'relation' &&
            // no tags
            Object.keys(entity.tags).length === 0 &&
            // no parent relations
            context.graph().parentRelations(entity).length === 0 &&
            // no members or one member with no role
            (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
        ) {
            // the user added this relation but didn't edit it at all, so just delete it
            var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
            context.perform(deleteAction, t('operations.delete.annotation.relation'));
        }
    };

    return mode;
}
<commit_msg>Revert "Only update selection styling for full redraws (re: #7706)"<commit_after>import { event as d3_event, select as d3_select } from 'd3-selection';

import { t } from '../core/localizer';

import { actionAddMidpoint } from '../actions/add_midpoint';
import { actionDeleteRelation } from '../actions/delete_relation';
import { actionMove } from '../actions/move';

import { behaviorBreathe } from '../behavior/breathe';
import { behaviorHover } from '../behavior/hover';
import { behaviorLasso } from '../behavior/lasso';
import { behaviorPaste } from '../behavior/paste';
import { behaviorSelect } from '../behavior/select';

import { operationMove } from '../operations/move';

import { geoExtent, geoChooseEdge } from '../geo';
import { modeBrowse } from './browse';
import { modeDragNode } from './drag_node';
import { modeDragNote } from './drag_note';
import { osmNode, osmWay } from '../osm';
import * as Operations from '../operations/index';
import { uiCmd } from '../ui/cmd';
import {
    utilArrayIntersection, utilDeepMemberSelector, utilEntityOrDeepMemberSelector,
    utilEntitySelector, utilKeybinding
} from '../util';

var _relatedParent;

export function modeSelect(context, selectedIDs) {
    var mode = {
        id: 'select',
        button: 'browse'
    };

    var keybinding = utilKeybinding('select');

    var _breatheBehavior = behaviorBreathe(context);
    var _modeDragNode = modeDragNode(context);
    var _selectBehavior;
    var _behaviors = [];

    var _operations = [];
    var _newFeature = false;
    var _follow = false;

    function singular() {
        if (selectedIDs && selectedIDs.length === 1) {
            return context.hasEntity(selectedIDs[0]);
        }
    }

    function selectedEntities() {
        return selectedIDs.map(function(id) {
            return context.hasEntity(id);
        }).filter(Boolean);
    }

    function checkSelectedIDs() {
        var ids = [];
        if (Array.isArray(selectedIDs)) {
            ids = selectedIDs.filter(function(id) {
                return context.hasEntity(id);
            });
        }

        if (!ids.length) {
            context.enter(modeBrowse(context));
            return false;
        } else if ((selectedIDs.length > 1 && ids.length === 1) ||
            (selectedIDs.length === 1 && ids.length > 1)) {
            // switch between single- and multi-select UI
            context.enter(modeSelect(context, ids));
            return false;
        }

        selectedIDs = ids;
        return true;
    }

    // find the common parent ways for nextVertex, previousVertex
    function commonParents() {
        var graph = context.graph();
        var commonParents = [];

        for (var i = 0; i < selectedIDs.length; i++) {
            var entity = context.hasEntity(selectedIDs[i]);
            if (!entity || entity.geometry(graph) !== 'vertex') {
                return [];  // selection includes some not vertexes
            }

            var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
            if (!commonParents.length) {
                commonParents = currParents;
                continue;
            }

            commonParents = utilArrayIntersection(commonParents, currParents);
            if (!commonParents.length) {
                return [];
            }
        }

        return commonParents;
    }

    function singularParent() {
        var parents = commonParents();
        if (!parents || parents.length === 0) {
            _relatedParent = null;
            return null;
        }

        // relatedParent is used when we visit a vertex with multiple
        // parents, and we want to remember which parent line we started on.

        if (parents.length === 1) {
            _relatedParent = parents[0];  // remember this parent for later
            return _relatedParent;
        }

        if (parents.indexOf(_relatedParent) !== -1) {
            return _relatedParent;   // prefer the previously seen parent
        }

        return parents[0];
    }

    mode.selectedIDs = function(val) {
        if (!arguments.length) return selectedIDs;
        selectedIDs = val;
        return mode;
    };

    mode.zoomToSelected = function() {
        context.map().zoomToEase(selectedEntities());
    };

    mode.newFeature = function(val) {
        if (!arguments.length) return _newFeature;
        _newFeature = val;
        return mode;
    };

    mode.selectBehavior = function(val) {
        if (!arguments.length) return _selectBehavior;
        _selectBehavior = val;
        return mode;
    };

    mode.follow = function(val) {
        if (!arguments.length) return _follow;
        _follow = val;
        return mode;
    };

    function loadOperations() {

        _operations.forEach(function(operation) {
            if (operation.behavior) {
                context.uninstall(operation.behavior);
            }
        });

        _operations = Object.values(Operations)
            .map(function(o) { return o(context, selectedIDs); })
            .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade'; });

        var downgradeOperation = Operations.operationDowngrade(context, selectedIDs);
        // don't allow delete if downgrade is available
        var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : Operations.operationDelete(context, selectedIDs);

        _operations.push(lastOperation);

        _operations.forEach(function(operation) {
            if (operation.behavior) {
                context.install(operation.behavior);
            }
        });

        // remove any displayed menu
        context.ui().closeEditMenu();
    }

    mode.operations = function() {
        return _operations;
    };

    mode.enter = function() {
        if (!checkSelectedIDs()) return;

        context.features().forceVisible(selectedIDs);

        _modeDragNode.restoreSelectedIDs(selectedIDs);

        loadOperations();

        if (!_behaviors.length) {
            if (!_selectBehavior) _selectBehavior = behaviorSelect(context);

            _behaviors = [
                behaviorPaste(context),
                _breatheBehavior,
                behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
                _selectBehavior,
                behaviorLasso(context),
                _modeDragNode.behavior,
                modeDragNote(context).behavior
            ];
        }
        _behaviors.forEach(context.install);

        keybinding
            .on(t('inspector.zoom_to.key'), mode.zoomToSelected)
            .on(['[', 'pgup'], previousVertex)
            .on([']', 'pgdown'], nextVertex)
            .on(['{', uiCmd('⌘['), 'home'], firstVertex)
            .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
            .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
            .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
            .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
            .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
            .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
            .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
            .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
            .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
            .on(['\\', 'pause'], nextParent)
            .on('⎋', esc, true);

        d3_select(document)
            .call(keybinding);

        context.ui().sidebar
            .select(selectedIDs, _newFeature);

        context.history()
            .on('change.select', function() {
                loadOperations();
                // reselect after change in case relation members were removed or added
                selectElements();
            })
            .on('undone.select', checkSelectedIDs)
            .on('redone.select', checkSelectedIDs);

        context.map()
            .on('drawn.select', selectElements)
            .on('crossEditableZoom.select', function() {
                selectElements();
                _breatheBehavior.restartIfNeeded(context.surface());
            });

        context.map().doubleUpHandler()
            .on('doubleUp.modeSelect', didDoubleUp);

        selectElements();

        if (_follow) {
            var extent = geoExtent();
            var graph = context.graph();
            selectedIDs.forEach(function(id) {
                var entity = context.entity(id);
                extent._extend(entity.extent(graph));
            });

            var loc = extent.center();
            context.map().centerEase(loc);
        }

        function nudgeSelection(delta) {
            return function() {
                // prevent nudging during low zoom selection
                if (!context.map().withinEditableZoom()) return;

                var moveOp = operationMove(context, selectedIDs);
                if (moveOp.disabled()) {
                    context.ui().flash
                        .duration(4000)
                        .iconName('#iD-operation-' + moveOp.id)
                        .iconClass('operation disabled')
                        .text(moveOp.tooltip)();
                } else {
                    context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
                }
            };
        }

        function didDoubleUp(loc) {
            if (!context.map().withinEditableZoom()) return;

            var target = d3_select(d3_event.target);

            var datum = target.datum();
            var entity = datum && datum.properties && datum.properties.entity;
            if (!entity) return;

            if (entity instanceof osmWay && target.classed('target')) {
                var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
                var prev = entity.nodes[choice.index - 1];
                var next = entity.nodes[choice.index];

                context.perform(
                    actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
                    t('operations.add.annotation.vertex')
                );

            } else if (entity.type === 'midpoint') {
                context.perform(
                    actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
                    t('operations.add.annotation.vertex'));
            }
        }

        function selectElements() {
            if (!checkSelectedIDs()) return;

            var surface = context.surface();

            surface.selectAll('.selected-member')
                .classed('selected-member', false);

            surface.selectAll('.selected')
                .classed('selected', false);

            surface.selectAll('.related')
                .classed('related', false);

            singularParent();
            if (_relatedParent) {
                surface.selectAll(utilEntitySelector([_relatedParent]))
                    .classed('related', true);
            }

            if (context.map().withinEditableZoom()) {
                // Apply selection styling if not in wide selection

                surface
                    .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
                    .classed('selected-member', true);
                surface
                    .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
                    .classed('selected', true);
            }

        }

        function esc() {
            if (context.container().select('.combobox').size()) return;
            context.enter(modeBrowse(context));
        }

        function firstVertex() {
            d3_event.preventDefault();
            var entity = singular();
            var parent = singularParent();
            var way;

            if (entity && entity.type === 'way') {
                way = entity;
            } else if (parent) {
                way = context.entity(parent);
            }

            if (way) {
                context.enter(
                    modeSelect(context, [way.first()]).follow(true)
                );
            }
        }

        function lastVertex() {
            d3_event.preventDefault();
            var entity = singular();
            var parent = singularParent();
            var way;

            if (entity && entity.type === 'way') {
                way = entity;
            } else if (parent) {
                way = context.entity(parent);
            }

            if (way) {
                context.enter(
                    modeSelect(context, [way.last()]).follow(true)
                );
            }
        }

        function previousVertex() {
            d3_event.preventDefault();
            var parent = singularParent();
            if (!parent) return;

            var way = context.entity(parent);
            var length = way.nodes.length;
            var curr = way.nodes.indexOf(selectedIDs[0]);
            var index = -1;

            if (curr > 0) {
                index = curr - 1;
            } else if (way.isClosed()) {
                index = length - 2;
            }

            if (index !== -1) {
                context.enter(
                    modeSelect(context, [way.nodes[index]]).follow(true)
                );
            }
        }

        function nextVertex() {
            d3_event.preventDefault();
            var parent = singularParent();
            if (!parent) return;

            var way = context.entity(parent);
            var length = way.nodes.length;
            var curr = way.nodes.indexOf(selectedIDs[0]);
            var index = -1;

            if (curr < length - 1) {
                index = curr + 1;
            } else if (way.isClosed()) {
                index = 0;
            }

            if (index !== -1) {
                context.enter(
                    modeSelect(context, [way.nodes[index]]).follow(true)
                );
            }
        }

        function nextParent() {
            d3_event.preventDefault();
            var parents = commonParents();
            if (!parents || parents.length < 2) return;

            var index = parents.indexOf(_relatedParent);
            if (index < 0 || index > parents.length - 2) {
                _relatedParent = parents[0];
            } else {
                _relatedParent = parents[index + 1];
            }

            var surface = context.surface();
            surface.selectAll('.related')
                .classed('related', false);

            if (_relatedParent) {
                surface.selectAll(utilEntitySelector([_relatedParent]))
                    .classed('related', true);
            }
        }
    };

    mode.exit = function() {

        _operations.forEach(function(operation) {
            if (operation.behavior) {
                context.uninstall(operation.behavior);
            }
        });
        _operations = [];

        _behaviors.forEach(context.uninstall);

        d3_select(document)
            .call(keybinding.unbind);

        context.ui().closeEditMenu();

        context.history()
            .on('change.select', null)
            .on('undone.select', null)
            .on('redone.select', null);

        var surface = context.surface();

        surface
            .selectAll('.selected-member')
            .classed('selected-member', false);

        surface
            .selectAll('.selected')
            .classed('selected', false);

        surface
            .selectAll('.highlighted')
            .classed('highlighted', false);

        surface
            .selectAll('.related')
            .classed('related', false);

        context.map().on('drawn.select', null);
        context.ui().sidebar.hide();
        context.features().forceVisible([]);

        var entity = singular();
        if (_newFeature && entity && entity.type === 'relation' &&
            // no tags
            Object.keys(entity.tags).length === 0 &&
            // no parent relations
            context.graph().parentRelations(entity).length === 0 &&
            // no members or one member with no role
            (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
        ) {
            // the user added this relation but didn't edit it at all, so just delete it
            var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
            context.perform(deleteAction, t('operations.delete.annotation.relation'));
        }
    };

    return mode;
}
<|endoftext|>


<commit_before>var Preferences = require('./../config/Preferences.js');

var Utility     = require('./Utility.js'),
    db          = require('./promised-db.js'),
    app         = require('./app.js'),
    hackyAssign = require('./hackyAssign.js'),
    autoAssign  = require('./autoAssign.js'),
    categorize  = require('./categorizeEvents.js');

var sendEmsUpdateNotifications = require('./sendEmsUpdateNotifications.js');

var request   = require('./promised-request.js'),
    dumpError = require('./dumpError.js');

var _       = require('underscore'),
    Q       = require('q'),
    cache   = require('memory-cache'),
    ejs     = require('ejs'),
    async   = require('async'),
    moment  = require('moment-range');

// takes two moment.js objects and gives the API url to make a request to.
var generateApiUrl = function(start, end) {
    start = start.format('YYYY/MM/DD'),
    end   = end.format('YYYY/MM/DD');
    return "http://webapps-test.wesleyan.edu/wapi/v1/public/ems/spec/booking_start/" + start + "/booking_end/" + end + "/";
};

//returns an object of the different fields.
//takes the value from the first object if different
var giveDifferenceOnFields = function(obj1, obj2, fieldList) {
    return fieldList.reduce(function(prev, field) {
        if(_.isEqual(obj1[field], obj2[field])) {
            return prev;
        } else {
            prev[field] = obj1[field];
            return prev;
        }
    }, {});
};

var processEvent = function(apiEvent) {
    if(_.isNull(apiEvent.notes)) {
        apiEvent.notes = '';
    }

    var multipleDaysEvent = false;
    if(apiEvent.booking_start_time.slice(-2) === 'PM' && apiEvent.booking_end_time.slice(-2) === 'AM') {
        multipleDaysEvent = true;
    }

    apiEvent.start      = new Date(apiEvent.booking_start_date + ' ' + apiEvent.booking_start_time);
    apiEvent.eventStart = new Date(apiEvent.booking_start_date + ' ' + apiEvent.event_start_time);
    // TODO: check if the event ends goes over midnight
    apiEvent.end        = new Date(apiEvent.booking_start_date + ' ' + apiEvent.booking_end_time);
    apiEvent.eventEnd   = new Date(apiEvent.booking_start_date + ' ' + apiEvent.event_end_time);

    if(multipleDaysEvent) {
        apiEvent.end      = moment(apiEvent.end).add('d', 1).toDate();
        apiEvent.eventEnd = moment(apiEvent.eventEnd).add('d', 1).toDate();
    }

    //onHold = true if the event is added before three days notice
    var onHold = moment().add('d', 3).endOf('day').isAfter(apiEvent.start);

    return {
        XMLid:        apiEvent.service_order_detail_id,
        title:        apiEvent.event_name,
        desc:         apiEvent.category !== 'A/V Services' ? apiEvent.notes : apiEvent.resources,
        loc:          apiEvent.room_description,
        start:        apiEvent.start,
        end:          apiEvent.end,
        eventStart:   apiEvent.eventStart,
        eventEnd:     apiEvent.eventEnd,
        cancelled:    apiEvent.booking_status === 'Cancelled',
        techMustStay: true,
        video:        ['video', 'recording'].reduce(function(prev, word) {
                          if (prev) {
                              return true;
                          } else {
                              return apiEvent.notes.indexOf(word) !== -1;
                          }
                      }, false),
        customer:     {
                          name: apiEvent.customer,
                          phone: apiEvent.customer_phone
                      },
        inventory:    [],
        notes:        [],
        shifts:       [],
        staffNeeded:  1,
        onHold:       onHold
    };
};

module.exports = function(cb) {
    var today         = moment().startOf('day'),
        twoWeeksLater = moment().add('w', 2).endOf('day');

    var apiEvents, dbEvents;

    // make a request to the API
    request(generateApiUrl(today,twoWeeksLater))
        .then(function(body) {
            apiEvents = JSON.parse(body).records.map(function(e) {
                e.service_order_detail_id = parseInt(e.service_order_detail_id);
                return e;
            });
            // fetch the events in the period from Spec database
            return db.events.find({'start': {$gte: today.toDate(), $lt: twoWeeksLater.toDate()}}).toArray();
        })
        .then(function(events) {
            dbEvents = events;
            // compare apiEvents to dbEvents,
            // map the list to return an object {status:'insert/update', event: eventObject}
            // delete the event from dbEvents if matched
            apiEvents = apiEvents.map(function(apiEvent) {
                //find the same event in dbEvents
                var correspondent = _.findWhere(dbEvents, {XMLid: apiEvent.service_order_detail_id});
                // delete correspondent from dbEvents
                dbEvents = dbEvents.filter(function(event) {
                    return (event.XMLid !== apiEvent.service_order_detail_id);
                });

                var processedEvent = processEvent(apiEvent);

                var fieldsToCheck = ['start', 'end', 'eventStart', 'eventEnd', 'desc', 'title', 'loc', 'cancelled'];

                var shouldInsert = _.isUndefined(correspondent);
                var shouldUpdate, eventDifference;

                if(!shouldInsert) {
                    eventDifference = giveDifferenceOnFields(processedEvent, correspondent, fieldsToCheck);
                    shouldUpdate = !_.isEqual(eventDifference, {});
                    if(correspondent.updated) {
                        //if the event info is updated at a point, the integration system shouldn't update it.
                        shouldUpdate = false;
                    }
                }

                if(shouldInsert) {
                    //insert event
                    return {
                        status: 'add',
                        event: hackyAssign(categorize(processedEvent)), //hacky auto staff assignment stuff
                    };
                } else if(shouldUpdate) {
                    //update event
                    return {
                        status: 'update',
                        event: processedEvent,
                        update: eventDifference
                    };
                } else {
                    return {
                        status: 'pass'
                    };
                }
            }).filter(function(item) {
                return item.status !== 'pass';
            });

            var changeNumbers = {add:0, update:0, remove: 0},
                parallel      = [],
                whatToReport  = {update:[], remove: dbEvents};

            //deal with apiEvents, insert or update accordingly
            apiEvents.forEach(function(obj) {
                if(obj.status === 'add') {
                    // Push a function to insert the event
                    parallel.push(function(callback) {
                        db.removedEvents.find({XMLid: obj.event.XMLid}, function(err, result) {
                            if(err) {console.log('Removed event could not be found');return;}
                            if(result.length > 0) {callback();return;} //This event has been added and removed before

                            db.events.save(obj.event, function(err, saved) {
                                if (err || !saved) {
                                    console.log("New event is not saved");
                                } else {
                                    changeNumbers.add++;
                                    callback();
                                }
                            });
                        });
                    });
                } else if(obj.status === 'update') {
                    parallel.push(function(callback) {
                        db.events.findAndModify({
                            query: {XMLid: obj.event.XMLid},
                            update: {$set: obj.update },
                            new: true
                        },
                        function(err, updated) {
                            if (err || !updated) {
                                console.log("Event could not be updated: " + err);
                                console.log(obj.event.title);
                            } else {
                                changeNumbers.update++;
                                whatToReport.update.push(updated);
                                callback();
                            }
                        });
                    });
                }
            });
            //the remaining events in dbEvents should be set cancelled.
            dbEvents.forEach(function(event) {
                parallel.push(function(callback) {
                    db.events.findAndModify({
                        query: {XMLid: event.XMLid},
                        update: {$set: {cancelled: true} },
                        new: true
                    },
                    function(err, updated) {
                        if (err || !updated) {
                            console.log("Event could not be called off: " + err);
                            console.log(event.title);
                        } else {
                            changeNumbers.remove++;
                            callback();
                        }
                    });
                });
            });

            async.parallel(parallel, function() {
                console.log(changeNumbers.add + ' events added, ' +
                            changeNumbers.update + ' updated and ' +
                            changeNumbers.remove + ' called off, ' +
                            'update process ended successfully.');

                // send notifications to the updated/called off event staff / managers
                /* sendEmsUpdateNotifications(
                    [{status:'insert', event:...}, {status:'update',event:...}, {status:'pass'}],
                    [{CANCELLED EVENT OBJECT}])
                   // the data structure of the first parameter can be seen above
                   // second parameter contains an array of cancelled event objects
                */
                sendEmsUpdateNotifications(apiEvents, dbEvents);

                // start auto assignment module
                autoAssign(function(count) {
                    if(!_.isUndefined(cb)) {
                        cb({update: changeNumbers, autoAssignCount: count});
                    }
                });
            });
        })
        .fail(function(err) { //triggered in any error
            dumpError(err);
        });
};
<commit_msg>Fixes multiple day event detection<commit_after>var Preferences = require('./../config/Preferences.js');

var Utility     = require('./Utility.js'),
    db          = require('./promised-db.js'),
    app         = require('./app.js'),
    hackyAssign = require('./hackyAssign.js'),
    autoAssign  = require('./autoAssign.js'),
    categorize  = require('./categorizeEvents.js');

var sendEmsUpdateNotifications = require('./sendEmsUpdateNotifications.js');

var request   = require('./promised-request.js'),
    dumpError = require('./dumpError.js');

var _       = require('underscore'),
    Q       = require('q'),
    cache   = require('memory-cache'),
    ejs     = require('ejs'),
    async   = require('async'),
    moment  = require('moment-range');

// takes two moment.js objects and gives the API url to make a request to.
var generateApiUrl = function(start, end) {
    start = start.format('YYYY/MM/DD'),
    end   = end.format('YYYY/MM/DD');
    return "http://webapps-test.wesleyan.edu/wapi/v1/public/ems/spec/booking_start/" + start + "/booking_end/" + end + "/";
};

//returns an object of the different fields.
//takes the value from the first object if different
var giveDifferenceOnFields = function(obj1, obj2, fieldList) {
    return fieldList.reduce(function(prev, field) {
        if(_.isEqual(obj1[field], obj2[field])) {
            return prev;
        } else {
            prev[field] = obj1[field];
            return prev;
        }
    }, {});
};

var processEvent = function(apiEvent) {
    if(_.isNull(apiEvent.notes)) {
        apiEvent.notes = '';
    }

    var multipleDaysEvent = false;

    var tempStart = moment((new Date()).toDateString() + " " + apiEvent.booking_start_time);
    var tempEnd   = moment((new Date()).toDateString() + " " + apiEvent.booking_end_time);

    // creating two temp vars with strings like "08:30 PM"
    // if end is before start, it's a 2 day event
    // since EMS API doesn't use a standard time format, we have to do this.
    if(tempEnd.isBefore(tempStart)) {
        multipleDaysEvent = true;
    }

    apiEvent.start      = new Date(apiEvent.booking_start_date + ' ' + apiEvent.booking_start_time);
    apiEvent.eventStart = new Date(apiEvent.booking_start_date + ' ' + apiEvent.event_start_time);
    // TODO: check if the event ends goes over midnight
    apiEvent.end        = new Date(apiEvent.booking_start_date + ' ' + apiEvent.booking_end_time);
    apiEvent.eventEnd   = new Date(apiEvent.booking_start_date + ' ' + apiEvent.event_end_time);

    if(multipleDaysEvent) {
        apiEvent.end      = moment(apiEvent.end).add('d', 1).toDate();
        apiEvent.eventEnd = moment(apiEvent.eventEnd).add('d', 1).toDate();
    }

    //onHold = true if the event is added before three days notice
    var onHold = moment().add('d', 3).endOf('day').isAfter(apiEvent.start);

    return {
        XMLid:        apiEvent.service_order_detail_id,
        title:        apiEvent.event_name,
        desc:         apiEvent.category !== 'A/V Services' ? apiEvent.notes : apiEvent.resources,
        loc:          apiEvent.room_description,
        start:        apiEvent.start,
        end:          apiEvent.end,
        eventStart:   apiEvent.eventStart,
        eventEnd:     apiEvent.eventEnd,
        cancelled:    apiEvent.booking_status === 'Cancelled',
        techMustStay: true,
        video:        ['video', 'recording'].reduce(function(prev, word) {
                          if (prev) {
                              return true;
                          } else {
                              return apiEvent.notes.indexOf(word) !== -1;
                          }
                      }, false),
        customer:     {
                          name: apiEvent.customer,
                          phone: apiEvent.customer_phone
                      },
        inventory:    [],
        notes:        [],
        shifts:       [],
        staffNeeded:  1,
        onHold:       onHold
    };
};

module.exports = function(cb) {
    var today         = moment().startOf('day'),
        twoWeeksLater = moment().add('w', 2).endOf('day');

    var apiEvents, dbEvents;

    // make a request to the API
    request(generateApiUrl(today,twoWeeksLater))
        .then(function(body) {
            apiEvents = JSON.parse(body).records.map(function(e) {
                e.service_order_detail_id = parseInt(e.service_order_detail_id);
                return e;
            });
            // fetch the events in the period from Spec database
            return db.events.find({'start': {$gte: today.toDate(), $lt: twoWeeksLater.toDate()}}).toArray();
        })
        .then(function(events) {
            dbEvents = events;
            // compare apiEvents to dbEvents,
            // map the list to return an object {status:'insert/update', event: eventObject}
            // delete the event from dbEvents if matched
            apiEvents = apiEvents.map(function(apiEvent) {
                //find the same event in dbEvents
                var correspondent = _.findWhere(dbEvents, {XMLid: apiEvent.service_order_detail_id});
                // delete correspondent from dbEvents
                dbEvents = dbEvents.filter(function(event) {
                    return (event.XMLid !== apiEvent.service_order_detail_id);
                });

                var processedEvent = processEvent(apiEvent);

                var fieldsToCheck = ['start', 'end', 'eventStart', 'eventEnd', 'desc', 'title', 'loc', 'cancelled'];

                var shouldInsert = _.isUndefined(correspondent);
                var shouldUpdate, eventDifference;

                if(!shouldInsert) {
                    eventDifference = giveDifferenceOnFields(processedEvent, correspondent, fieldsToCheck);
                    shouldUpdate = !_.isEqual(eventDifference, {});
                    if(correspondent.updated) {
                        //if the event info is updated at a point, the integration system shouldn't update it.
                        shouldUpdate = false;
                    }
                }

                if(shouldInsert) {
                    //insert event
                    return {
                        status: 'add',
                        event: hackyAssign(categorize(processedEvent)), //hacky auto staff assignment stuff
                    };
                } else if(shouldUpdate) {
                    //update event
                    return {
                        status: 'update',
                        event: processedEvent,
                        update: eventDifference
                    };
                } else {
                    return {
                        status: 'pass'
                    };
                }
            }).filter(function(item) {
                return item.status !== 'pass';
            });

            var changeNumbers = {add:0, update:0, remove: 0},
                parallel      = [],
                whatToReport  = {update:[], remove: dbEvents};

            //deal with apiEvents, insert or update accordingly
            apiEvents.forEach(function(obj) {
                if(obj.status === 'add') {
                    // Push a function to insert the event
                    parallel.push(function(callback) {
                        db.removedEvents.find({XMLid: obj.event.XMLid}, function(err, result) {
                            if(err) {console.log('Removed event could not be found');return;}
                            if(result.length > 0) {callback();return;} //This event has been added and removed before

                            db.events.save(obj.event, function(err, saved) {
                                if (err || !saved) {
                                    console.log("New event is not saved");
                                } else {
                                    changeNumbers.add++;
                                    callback();
                                }
                            });
                        });
                    });
                } else if(obj.status === 'update') {
                    parallel.push(function(callback) {
                        db.events.findAndModify({
                            query: {XMLid: obj.event.XMLid},
                            update: {$set: obj.update },
                            new: true
                        },
                        function(err, updated) {
                            if (err || !updated) {
                                console.log("Event could not be updated: " + err);
                                console.log(obj.event.title);
                            } else {
                                changeNumbers.update++;
                                whatToReport.update.push(updated);
                                callback();
                            }
                        });
                    });
                }
            });
            //the remaining events in dbEvents should be set cancelled.
            dbEvents.forEach(function(event) {
                parallel.push(function(callback) {
                    db.events.findAndModify({
                        query: {XMLid: event.XMLid},
                        update: {$set: {cancelled: true} },
                        new: true
                    },
                    function(err, updated) {
                        if (err || !updated) {
                            console.log("Event could not be called off: " + err);
                            console.log(event.title);
                        } else {
                            changeNumbers.remove++;
                            callback();
                        }
                    });
                });
            });

            async.parallel(parallel, function() {
                console.log(changeNumbers.add + ' events added, ' +
                            changeNumbers.update + ' updated and ' +
                            changeNumbers.remove + ' called off, ' +
                            'update process ended successfully.');

                // send notifications to the updated/called off event staff / managers
                /* sendEmsUpdateNotifications(
                    [{status:'insert', event:...}, {status:'update',event:...}, {status:'pass'}],
                    [{CANCELLED EVENT OBJECT}])
                   // the data structure of the first parameter can be seen above
                   // second parameter contains an array of cancelled event objects
                */
                sendEmsUpdateNotifications(apiEvents, dbEvents);

                // start auto assignment module
                autoAssign(function(count) {
                    if(!_.isUndefined(cb)) {
                        cb({update: changeNumbers, autoAssignCount: count});
                    }
                });
            });
        })
        .fail(function(err) { //triggered in any error
            dumpError(err);
        });
};
<|endoftext|>


<commit_before>const h = require('../h')
const fs = require('fs')
const { Value, when } = require('@mmckegg/mutant')
const u = require('../util')
const pull = require('pull-stream')
const Scroller = require('pull-scroll')
const TextNodeSearcher = require('text-node-searcher')

exports.needs = {
  build_scroller: 'first',
  message_render: 'first',
  sbot_log: 'first',
  sbot_fulltext_search: 'first'
}

exports.gives = {
  screen_view: true,
  mcss: true
}

var whitespace = /\s+/

function andSearch(terms, inputs) {
  for(var i = 0; i < terms.length; i++) {
    var match = false
    for(var j = 0; j < inputs.length; j++) {
      if(terms[i].test(inputs[j])) match = true
    }
    //if a term was not matched by anything, filter this one
    if(!match) return false
  }
  return true
}

function searchFilter(terms) {
  return function (msg) {
    var c = msg && msg.value && msg.value.content
    return c && (
      msg.key == terms[0] ||
      andSearch(terms.map(function (term) {
        return new RegExp('\\b'+term+'\\b', 'i')
      }), [c.text, c.name, c.title])
    )
  }
}

function createOrRegExp(ary) {
  return new RegExp(ary.map(function (e) {
    return '\\b'+e+'\\b'
  }).join('|'), 'i')
}

function highlight(el, query) {
  var searcher = new TextNodeSearcher({container: el})
  searcher.query = query
  searcher.highlight()
  return el
}

function fallback(reader) {
  var fallbackRead
  return function (read) {
    return function (abort, cb) {
      read(abort, function next(end, data) {
        if (end && reader && (fallbackRead = reader(end))) {
          reader = null
          read = fallbackRead
          read(abort, next)
        } else {
          cb(end, data)
        }
      })
    }
  }
}

exports.create = function (api) {

  return {
    screen_view,
    mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8')
  }

  function screen_view (path) {
    if (path[0] !== '?') return

    var queryStr = path.substr(1).trim()
    var query = queryStr.split(whitespace)
    var _matches = searchFilter(query)

    const isLinearSearch = Value(false)
    const searched = Value(0)
    const matches = Value(0)
    const searchHeader = h('Search', [
      h('header', h('h1', query.join(' '))),
      when(isLinearSearch,
        h('section.details', [
          h('div.searched', ['Searched: ', searched]),
          h('div.matches', [matches, ' matches'])
        ])
      )
    ])
    var { container, content } = api.build_scroller({ prepend: searchHeader })
    container.id = path // helps tabs find this tab

    function matchesQuery (data) {
      searched.set(searched() + 1)
      var m = _matches(data)
      if(m) matches.set(matches() +1 )

      return m
    }

    function renderMsg(msg) {
      var el = api.message_render(msg)
      highlight(el, createOrRegExp(query))
      return el
    }

    pull(
      api.sbot_log({old: false}),
      pull.filter(matchesQuery),
      Scroller(container, content, renderMsg, true, false)
    )

    pull(
      u.next(api.sbot_fulltext_search, {query: queryStr, reverse: true, limit: 500, live: false}),
      fallback((err) => {
        if (/^no source/.test(err.message)) {
          isLinearSearch.set(true)
          return pull(
            u.next(api.sbot_log, {reverse: true, limit: 500, live: false}),
            pull.filter(matchesQuery)
          )
        }
      }),
      Scroller(container, content, renderMsg, false, false)
    )

    return container
  }
}

<commit_msg>Show when there are no fulltext search matches<commit_after>const h = require('../h')
const fs = require('fs')
const { Value, when, computed } = require('@mmckegg/mutant')
const u = require('../util')
const pull = require('pull-stream')
const Scroller = require('pull-scroll')
const TextNodeSearcher = require('text-node-searcher')

exports.needs = {
  build_scroller: 'first',
  message_render: 'first',
  sbot_log: 'first',
  sbot_fulltext_search: 'first'
}

exports.gives = {
  screen_view: true,
  mcss: true
}

var whitespace = /\s+/

function andSearch(terms, inputs) {
  for(var i = 0; i < terms.length; i++) {
    var match = false
    for(var j = 0; j < inputs.length; j++) {
      if(terms[i].test(inputs[j])) match = true
    }
    //if a term was not matched by anything, filter this one
    if(!match) return false
  }
  return true
}

function searchFilter(terms) {
  return function (msg) {
    var c = msg && msg.value && msg.value.content
    return c && (
      msg.key == terms[0] ||
      andSearch(terms.map(function (term) {
        return new RegExp('\\b'+term+'\\b', 'i')
      }), [c.text, c.name, c.title])
    )
  }
}

function createOrRegExp(ary) {
  return new RegExp(ary.map(function (e) {
    return '\\b'+e+'\\b'
  }).join('|'), 'i')
}

function highlight(el, query) {
  var searcher = new TextNodeSearcher({container: el})
  searcher.query = query
  searcher.highlight()
  return el
}

function fallback(reader) {
  var fallbackRead
  return function (read) {
    return function (abort, cb) {
      read(abort, function next(end, data) {
        if (end && reader && (fallbackRead = reader(end))) {
          reader = null
          read = fallbackRead
          read(abort, next)
        } else {
          cb(end, data)
        }
      })
    }
  }
}

exports.create = function (api) {

  return {
    screen_view,
    mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8')
  }

  function screen_view (path) {
    if (path[0] !== '?') return

    var queryStr = path.substr(1).trim()
    var query = queryStr.split(whitespace)
    var matchesQuery = searchFilter(query)

    const isLinearSearch = Value(false)
    const isFulltextSearchDone = Value(false)
    const searched = Value(0)
    const matches = Value(0)
    const hasNoFulltextMatches = computed([isFulltextSearchDone, matches],
      (done, matches) => done && matches === 0)
    const searchHeader = h('Search', [
      h('header', h('h1', query.join(' '))),
      when(isLinearSearch,
        h('section.details', [
          h('div.searched', ['Searched: ', searched]),
          h('div.matches', [matches, ' matches'])
        ]),
        when(hasNoFulltextMatches,
          h('section.details', [
            h('div.matches', 'No matches')
          ])
        )
      )
    ])
    var { container, content } = api.build_scroller({ prepend: searchHeader })
    container.id = path // helps tabs find this tab

    function renderMsg(msg) {
      var el = api.message_render(msg)
      highlight(el, createOrRegExp(query))
      return el
    }

    pull(
      api.sbot_log({old: false}),
      pull.filter(matchesQuery),
      Scroller(container, content, renderMsg, true, false)
    )

    pull(
      u.next(api.sbot_fulltext_search, {query: queryStr, reverse: true, limit: 500, live: false}),
      fallback((err) => {
        if (err === true) {
          isFulltextSearchDone.set(true)
        } else if (/^no source/.test(err.message)) {
          isLinearSearch.set(true)
          return pull(
            u.next(api.sbot_log, {reverse: true, limit: 500, live: false}),
            pull.through(() => searched.set(searched()+1)),
            pull.filter(matchesQuery)
          )
        }
      }),
      pull.through(() => matches.set(matches()+1)),
      Scroller(container, content, renderMsg, false, false)
    )

    return container
  }
}

<|endoftext|>


<commit_before>
// https://www.npmjs.com/package/node-sass

module.exports = {

  /**
   * outputFilename: The filename of the saved CSS file
   * from the sass build. The directory which it is saved in
   * is set within the "buildDir" config options.
   */
  outputFilename: process.env.IONIC_OUTPUT_CSS_FILE_NAME,

  /**
   * sourceMap: If source map should be built or not.
   */
  sourceMap: false,

  /**
   * outputStyle: How node-sass should output the css file.
   */
  outputStyle: 'expanded',

  /**
   * autoprefixer: The config options for autoprefixer.
   * Excluding this config will skip applying autoprefixer.
   * https://www.npmjs.com/package/autoprefixer
   */
  autoprefixer: {
    browsers: [
      'last 2 versions',
      'iOS >= 8',
      'Android >= 4.4',
      'Explorer >= 11',
      'ExplorerMobile >= 11'
    ],
    cascade: false
  },

  /**
   * includePaths: Used by node-sass for additional
   * paths to search for sass imports by just name.
   */
  includePaths: [
    'node_modules/ionic-angular/themes',
    'node_modules/ionicons/dist/scss',
    'node_modules/ionic-angular/fonts'
  ],

  /**
   * includeFiles: An array of regex patterns to search for
   * sass files in the same directory as the component module.
   * If a file matches both include and exclude patterns, then
   * the file will be excluded.
   */
  includeFiles: [
    /\.(s(c|a)ss)$/i
  ],

  /**
   * excludeFiles: An array of regex patterns for files which
   * should be excluded. If a file matches both include and exclude
   * patterns, then the file will be excluded.
   */
  excludeFiles: [
    /*  /\.(wp).(scss)$/i  */
  ],

  /**
   * variableSassFiles: Lists out the files which include
   * only sass variables. These variables are the first sass files
   * to be imported so their values override default variables.
   */
  variableSassFiles: [
    '{{SRC}}/theme/variables.scss'
  ],

  /**
   * directoryMaps: Compiled JS modules may be within a different
   * directory than its source file and sibling component sass files.
   * For example, NGC places it's files within the .tmp directory
   * but doesn't copy over its sass files. This is useful so sass
   * also checks the JavaScript's source directory for sass files.
   */
  directoryMaps: {
    '{{TMP}}': '{{SRC}}'
  },

  /**
   * excludeModules: Used just as a way to skip over
   * modules which we know wouldn't have any sass to be
   * bundled. "excludeModules" isn't necessary, but is a
   * good way to speed up build times by skipping modules.
   */
  excludeModules: [
    '@angular',
    'commonjs-proxy',
    'core-js',
    'ionic-native',
    'rxjs',
    'zone.js'
  ]

};
<commit_msg>chore(ionicons): remove the import of ionicons css (#669)<commit_after>
// https://www.npmjs.com/package/node-sass

module.exports = {

  /**
   * outputFilename: The filename of the saved CSS file
   * from the sass build. The directory which it is saved in
   * is set within the "buildDir" config options.
   */
  outputFilename: process.env.IONIC_OUTPUT_CSS_FILE_NAME,

  /**
   * sourceMap: If source map should be built or not.
   */
  sourceMap: false,

  /**
   * outputStyle: How node-sass should output the css file.
   */
  outputStyle: 'expanded',

  /**
   * autoprefixer: The config options for autoprefixer.
   * Excluding this config will skip applying autoprefixer.
   * https://www.npmjs.com/package/autoprefixer
   */
  autoprefixer: {
    browsers: [
      'last 2 versions',
      'iOS >= 8',
      'Android >= 4.4',
      'Explorer >= 11',
      'ExplorerMobile >= 11'
    ],
    cascade: false
  },

  /**
   * includePaths: Used by node-sass for additional
   * paths to search for sass imports by just name.
   */
  includePaths: [
    'node_modules/ionic-angular/themes',
    'node_modules/ionic-angular/fonts'
  ],

  /**
   * includeFiles: An array of regex patterns to search for
   * sass files in the same directory as the component module.
   * If a file matches both include and exclude patterns, then
   * the file will be excluded.
   */
  includeFiles: [
    /\.(s(c|a)ss)$/i
  ],

  /**
   * excludeFiles: An array of regex patterns for files which
   * should be excluded. If a file matches both include and exclude
   * patterns, then the file will be excluded.
   */
  excludeFiles: [
    /*  /\.(wp).(scss)$/i  */
  ],

  /**
   * variableSassFiles: Lists out the files which include
   * only sass variables. These variables are the first sass files
   * to be imported so their values override default variables.
   */
  variableSassFiles: [
    '{{SRC}}/theme/variables.scss'
  ],

  /**
   * directoryMaps: Compiled JS modules may be within a different
   * directory than its source file and sibling component sass files.
   * For example, NGC places it's files within the .tmp directory
   * but doesn't copy over its sass files. This is useful so sass
   * also checks the JavaScript's source directory for sass files.
   */
  directoryMaps: {
    '{{TMP}}': '{{SRC}}'
  },

  /**
   * excludeModules: Used just as a way to skip over
   * modules which we know wouldn't have any sass to be
   * bundled. "excludeModules" isn't necessary, but is a
   * good way to speed up build times by skipping modules.
   */
  excludeModules: [
    '@angular',
    'commonjs-proxy',
    'core-js',
    'ionic-native',
    'rxjs',
    'zone.js'
  ]

};
<|endoftext|>


<commit_before>/**
 * CONSTANTS
*/
const ADD_FILTER = 'widgetEditor/ADD_FILTER';
const SET_FILTER_VALUE = 'widgetEditor/SET_FILTER_VALUE';
const SET_FILTERS = 'widgetEditor/SET_FILTERS';
const REMOVE_FILTER = 'widgetEditor/REMOVE_FILTER';
const SET_COLOR = 'widgetEditor/SET_COLOR';
const REMOVE_COLOR = 'widgetEditor/REMOVE_COLOR';
const SET_SIZE = 'widgetEditor/SET_SIZE';
const REMOVE_SIZE = 'widgetEditor/REMOVE_SIZE';
const SET_CATEGORY = 'widgetEditor/SET_CATEGORY';
const REMOVE_CATEGORY = 'widgetEditor/REMOVE_CATEGORY';
const SET_VALUE = 'widgetEditor/SET_VALUE';
const REMOVE_VALUE = 'widgetEditor/REMOVE_VALUE';
const SET_CHART_TYPE = 'widgetEditor/SET_CHART_TYPE';
const SET_AGGREGATE_FN = 'widgetEditor/SET_AGGREGATE_FN';
const SET_ORDER_BY = 'widgetEditor/SET_ORDER_BY';
const REMOVE_ORDER_BY = 'widgetEditor/REMOVE_ORDER_BY';
const SHOW_LAYER = 'widgetEditor/SHOW_LAYER';
const SET_FIELDS = 'widgetEditor/SET_FIELDS';
const SET_LIMIT = 'widgetEditor/SET_LIMIT';
const RESET = 'widgetEditor/RESET';

/**
 * REDUCER
*/
const initialState = {
  aggregateFunction: null,
  orderBy: null,
  filters: [],
  color: null,
  size: null,
  category: null,
  value: null,
  layer: null,
  fields: [],
  limit: 1000
};

export default function (state = initialState, action) {
  switch (action.type) {
    case ADD_FILTER: {
      const filters = state.filters.slice(0);
      const element = action.payload;
      const found = filters.find(val => val.name === element.name);
      if (!found) {
        filters.push(action.payload);
      }
      return Object.assign({}, state, {
        filters
      });
    }

    case SET_FILTER_VALUE: {
      const index = state.filters.findIndex(f => f.name === action.payload.name);
      const filters = state.filters.map((filter, i) => {
        if (i !== index) return filter;
        return Object.assign({}, filter, {
          value: action.payload.value,
          notNull: action.payload.notNull
        });
      });
      return Object.assign({}, state, { filters });
    }

    case REMOVE_FILTER: {
      const filters = state.filters.slice(0);
      const element = action.payload;
      let index = 0;
      for (let i = 0; i < filters.length; i++) {
        if (filters[i].name === element.name) {
          index = i;
        }
      }
      filters.splice(index, 1);
      return Object.assign({}, state, {
        filters
      });
    }

    case SET_COLOR: {
      return Object.assign({}, state, {
        color: action.payload
      });
    }

    case REMOVE_COLOR: {
      return Object.assign({}, state, {
        color: null
      });
    }

    case SET_SIZE: {
      return Object.assign({}, state, {
        size: action.payload
      });
    }

    case REMOVE_SIZE: {
      return Object.assign({}, state, {
        size: null
      });
    }

    case SET_CATEGORY: {
      return Object.assign({}, state, {
        category: action.payload
      });
    }

    case REMOVE_CATEGORY: {
      return Object.assign({}, state, {
        category: null
      });
    }

    case SET_VALUE: {
      return Object.assign({}, state, {
        value: action.payload
      });
    }

    case REMOVE_VALUE: {
      return Object.assign({}, state, {
        value: null
      });
    }

    case SET_ORDER_BY: {
      return Object.assign({}, state, {
        orderBy: action.payload
      });
    }

    case REMOVE_ORDER_BY: {
      return Object.assign({}, state, {
        orderBy: null
      });
    }

    case SET_CHART_TYPE: {
      return Object.assign({}, state, {
        chartType: action.payload
      });
    }

    case SET_AGGREGATE_FN: {
      return Object.assign({}, state, {
        aggregateFunction: action.payload
      });
    }

    case RESET: {
      return Object.assign({}, initialState);
    }

    case SHOW_LAYER: {
      return Object.assign({}, state, {
        layer: action.payload
      });
    }

    case SET_FIELDS: {
      return Object.assign({}, state, {
        fields: action.payload
      });
    }

    case SET_LIMIT: {
      return Object.assign({}, state, {
        limit: action.payload
      });
    }

    case SET_FILTERS: {
      return Object.assign({}, state, {
        filters: action.payload
      });
    }

    default:
      return state;
  }
}

/**
 * ACTIONS
 * - addFilter
 * - setFilterValue
 * - removeFilter
 * - setFilters
 * - setColor
 * - removeColor
 * - setSize
 * - removeSize
 * - setCategory
 * - removeCategory
 * - setValue
 * - removeValue
 * - setChartType
 * - resetWidgetEditor
 * - showLayer
 * - setFields
 * - setOrderBy
 * - removeOrderBy
 * - setLimit
*/
export function addFilter(filter) {
  return dispatch => dispatch({ type: ADD_FILTER, payload: filter });
}
export function setFilterValue(name, value, notNull) {
  return dispatch => dispatch({ type: SET_FILTER_VALUE, payload: { name, value, notNull } });
}
export function setFilters(filters) {
  return dispatch => dispatch({ type: SET_FILTERS, payload: filters });
}
export function removeFilter(filter) {
  return dispatch => dispatch({ type: REMOVE_FILTER, payload: filter });
}
export function setColor(color) {
  return dispatch => dispatch({ type: SET_COLOR, payload: color });
}
export function removeColor(color) {
  return dispatch => dispatch({ type: REMOVE_COLOR, payload: color });
}
export function setSize(size) {
  return dispatch => dispatch({ type: SET_SIZE, payload: size });
}
export function removeSize(size) {
  return dispatch => dispatch({ type: REMOVE_SIZE, payload: size });
}
export function setCategory(category) {
  return dispatch => dispatch({ type: SET_CATEGORY, payload: category });
}
export function removeCategory(category) {
  return dispatch => dispatch({ type: REMOVE_CATEGORY, payload: category });
}
export function setValue(value) {
  return dispatch => dispatch({ type: SET_VALUE, payload: value });
}
export function removeValue(value) {
  return dispatch => dispatch({ type: REMOVE_VALUE, payload: value });
}
export function setOrderBy(orderBy) {
  return dispatch => dispatch({ type: SET_ORDER_BY, payload: orderBy });
}
export function removeOrderBy(orderBy) {
  return dispatch => dispatch({ type: REMOVE_ORDER_BY, payload: orderBy });
}
export function setChartType(type) {
  return dispatch => dispatch({ type: SET_CHART_TYPE, payload: type });
}
export function setAggregateFunction(value) {
  return dispatch => dispatch({ type: SET_AGGREGATE_FN, payload: value });
}
export function resetWidgetEditor() {
  return dispatch => dispatch({ type: RESET });
}
export function showLayer(layer) {
  return dispatch => dispatch({ type: SHOW_LAYER, payload: layer });
}
export function setFields(layer) {
  return dispatch => dispatch({ type: SET_FIELDS, payload: layer });
}
export function setLimit(limit) {
  return dispatch => dispatch({ type: SET_LIMIT, payload: limit });
}
<commit_msg>hotfix: 500 limit in widgetEditor reducer<commit_after>/**
 * CONSTANTS
*/
const ADD_FILTER = 'widgetEditor/ADD_FILTER';
const SET_FILTER_VALUE = 'widgetEditor/SET_FILTER_VALUE';
const SET_FILTERS = 'widgetEditor/SET_FILTERS';
const REMOVE_FILTER = 'widgetEditor/REMOVE_FILTER';
const SET_COLOR = 'widgetEditor/SET_COLOR';
const REMOVE_COLOR = 'widgetEditor/REMOVE_COLOR';
const SET_SIZE = 'widgetEditor/SET_SIZE';
const REMOVE_SIZE = 'widgetEditor/REMOVE_SIZE';
const SET_CATEGORY = 'widgetEditor/SET_CATEGORY';
const REMOVE_CATEGORY = 'widgetEditor/REMOVE_CATEGORY';
const SET_VALUE = 'widgetEditor/SET_VALUE';
const REMOVE_VALUE = 'widgetEditor/REMOVE_VALUE';
const SET_CHART_TYPE = 'widgetEditor/SET_CHART_TYPE';
const SET_AGGREGATE_FN = 'widgetEditor/SET_AGGREGATE_FN';
const SET_ORDER_BY = 'widgetEditor/SET_ORDER_BY';
const REMOVE_ORDER_BY = 'widgetEditor/REMOVE_ORDER_BY';
const SHOW_LAYER = 'widgetEditor/SHOW_LAYER';
const SET_FIELDS = 'widgetEditor/SET_FIELDS';
const SET_LIMIT = 'widgetEditor/SET_LIMIT';
const RESET = 'widgetEditor/RESET';

/**
 * REDUCER
*/
const initialState = {
  aggregateFunction: null,
  orderBy: null,
  filters: [],
  color: null,
  size: null,
  category: null,
  value: null,
  layer: null,
  fields: [],
  limit: 500
};

export default function (state = initialState, action) {
  switch (action.type) {
    case ADD_FILTER: {
      const filters = state.filters.slice(0);
      const element = action.payload;
      const found = filters.find(val => val.name === element.name);
      if (!found) {
        filters.push(action.payload);
      }
      return Object.assign({}, state, {
        filters
      });
    }

    case SET_FILTER_VALUE: {
      const index = state.filters.findIndex(f => f.name === action.payload.name);
      const filters = state.filters.map((filter, i) => {
        if (i !== index) return filter;
        return Object.assign({}, filter, {
          value: action.payload.value,
          notNull: action.payload.notNull
        });
      });
      return Object.assign({}, state, { filters });
    }

    case REMOVE_FILTER: {
      const filters = state.filters.slice(0);
      const element = action.payload;
      let index = 0;
      for (let i = 0; i < filters.length; i++) {
        if (filters[i].name === element.name) {
          index = i;
        }
      }
      filters.splice(index, 1);
      return Object.assign({}, state, {
        filters
      });
    }

    case SET_COLOR: {
      return Object.assign({}, state, {
        color: action.payload
      });
    }

    case REMOVE_COLOR: {
      return Object.assign({}, state, {
        color: null
      });
    }

    case SET_SIZE: {
      return Object.assign({}, state, {
        size: action.payload
      });
    }

    case REMOVE_SIZE: {
      return Object.assign({}, state, {
        size: null
      });
    }

    case SET_CATEGORY: {
      return Object.assign({}, state, {
        category: action.payload
      });
    }

    case REMOVE_CATEGORY: {
      return Object.assign({}, state, {
        category: null
      });
    }

    case SET_VALUE: {
      return Object.assign({}, state, {
        value: action.payload
      });
    }

    case REMOVE_VALUE: {
      return Object.assign({}, state, {
        value: null
      });
    }

    case SET_ORDER_BY: {
      return Object.assign({}, state, {
        orderBy: action.payload
      });
    }

    case REMOVE_ORDER_BY: {
      return Object.assign({}, state, {
        orderBy: null
      });
    }

    case SET_CHART_TYPE: {
      return Object.assign({}, state, {
        chartType: action.payload
      });
    }

    case SET_AGGREGATE_FN: {
      return Object.assign({}, state, {
        aggregateFunction: action.payload
      });
    }

    case RESET: {
      return Object.assign({}, initialState);
    }

    case SHOW_LAYER: {
      return Object.assign({}, state, {
        layer: action.payload
      });
    }

    case SET_FIELDS: {
      return Object.assign({}, state, {
        fields: action.payload
      });
    }

    case SET_LIMIT: {
      return Object.assign({}, state, {
        limit: action.payload
      });
    }

    case SET_FILTERS: {
      return Object.assign({}, state, {
        filters: action.payload
      });
    }

    default:
      return state;
  }
}

/**
 * ACTIONS
 * - addFilter
 * - setFilterValue
 * - removeFilter
 * - setFilters
 * - setColor
 * - removeColor
 * - setSize
 * - removeSize
 * - setCategory
 * - removeCategory
 * - setValue
 * - removeValue
 * - setChartType
 * - resetWidgetEditor
 * - showLayer
 * - setFields
 * - setOrderBy
 * - removeOrderBy
 * - setLimit
*/
export function addFilter(filter) {
  return dispatch => dispatch({ type: ADD_FILTER, payload: filter });
}
export function setFilterValue(name, value, notNull) {
  return dispatch => dispatch({ type: SET_FILTER_VALUE, payload: { name, value, notNull } });
}
export function setFilters(filters) {
  return dispatch => dispatch({ type: SET_FILTERS, payload: filters });
}
export function removeFilter(filter) {
  return dispatch => dispatch({ type: REMOVE_FILTER, payload: filter });
}
export function setColor(color) {
  return dispatch => dispatch({ type: SET_COLOR, payload: color });
}
export function removeColor(color) {
  return dispatch => dispatch({ type: REMOVE_COLOR, payload: color });
}
export function setSize(size) {
  return dispatch => dispatch({ type: SET_SIZE, payload: size });
}
export function removeSize(size) {
  return dispatch => dispatch({ type: REMOVE_SIZE, payload: size });
}
export function setCategory(category) {
  return dispatch => dispatch({ type: SET_CATEGORY, payload: category });
}
export function removeCategory(category) {
  return dispatch => dispatch({ type: REMOVE_CATEGORY, payload: category });
}
export function setValue(value) {
  return dispatch => dispatch({ type: SET_VALUE, payload: value });
}
export function removeValue(value) {
  return dispatch => dispatch({ type: REMOVE_VALUE, payload: value });
}
export function setOrderBy(orderBy) {
  return dispatch => dispatch({ type: SET_ORDER_BY, payload: orderBy });
}
export function removeOrderBy(orderBy) {
  return dispatch => dispatch({ type: REMOVE_ORDER_BY, payload: orderBy });
}
export function setChartType(type) {
  return dispatch => dispatch({ type: SET_CHART_TYPE, payload: type });
}
export function setAggregateFunction(value) {
  return dispatch => dispatch({ type: SET_AGGREGATE_FN, payload: value });
}
export function resetWidgetEditor() {
  return dispatch => dispatch({ type: RESET });
}
export function showLayer(layer) {
  return dispatch => dispatch({ type: SHOW_LAYER, payload: layer });
}
export function setFields(layer) {
  return dispatch => dispatch({ type: SET_FIELDS, payload: layer });
}
export function setLimit(limit) {
  return dispatch => dispatch({ type: SET_LIMIT, payload: limit });
}
<|endoftext|>